Skip to content

Commit 88e3dce

Browse files
[Workflow] Add weighted transitions
1 parent c805014 commit 88e3dce

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

workflow.rst

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,226 @@ when needed and vice-versa when working with your objects::
502502
// ...
503503
};
504504
505+
Using Weighted Transitions
506+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
507+
508+
Weighted transitions allow you to define transitions where multiple tokens (instances)
509+
are consumed from or produced to places. This is useful for modeling complex workflows
510+
such as manufacturing processes, resource allocation, or any scenario where multiple
511+
instances of something need to be produced or consumed.
512+
513+
.. versionadded:: 7.4
514+
515+
The support for weighted transitions was introduced in Symfony 7.4.
516+
517+
For example, imagine a table-making workflow where you need to create 4 legs, 1 top,
518+
and track the process with a stopwatch. You can use weighted transitions to model this:
519+
520+
.. configuration-block::
521+
522+
.. code-block:: yaml
523+
524+
# config/packages/workflow.yaml
525+
framework:
526+
workflows:
527+
make_table:
528+
type: 'workflow'
529+
marking_store:
530+
type: 'method'
531+
property: 'marking'
532+
supports:
533+
- App\Entity\TableProject
534+
initial_marking: init
535+
places:
536+
- init
537+
- prepare_leg
538+
- prepare_top
539+
- stopwatch_running
540+
- leg_created
541+
- top_created
542+
- finished
543+
transitions:
544+
start:
545+
from: init
546+
to:
547+
- place: prepare_leg
548+
weight: 4
549+
- place: prepare_top
550+
weight: 1
551+
- place: stopwatch_running
552+
weight: 1
553+
build_leg:
554+
from: prepare_leg
555+
to: leg_created
556+
build_top:
557+
from: prepare_top
558+
to: top_created
559+
join:
560+
from:
561+
- place: leg_created
562+
weight: 4
563+
- top_created # weight defaults to 1
564+
- stopwatch_running
565+
to: finished
566+
567+
.. code-block:: xml
568+
569+
<!-- config/packages/workflow.xml -->
570+
<?xml version="1.0" encoding="UTF-8" ?>
571+
<container xmlns="http://symfony.com/schema/dic/services"
572+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
573+
xmlns:framework="http://symfony.com/schema/dic/symfony"
574+
xsi:schemaLocation="http://symfony.com/schema/dic/services
575+
https://symfony.com/schema/dic/services/services-1.0.xsd
576+
http://symfony.com/schema/dic/symfony
577+
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
578+
579+
<framework:config>
580+
<framework:workflow name="make_table" type="workflow">
581+
<framework:marking-store type="method">
582+
<framework:argument>marking</framework:argument>
583+
</framework:marking-store>
584+
<framework:support>App\Entity\TableProject</framework:support>
585+
<framework:initial-marking>init</framework:initial-marking>
586+
587+
<framework:place>init</framework:place>
588+
<framework:place>prepare_leg</framework:place>
589+
<framework:place>prepare_top</framework:place>
590+
<framework:place>stopwatch_running</framework:place>
591+
<framework:place>leg_created</framework:place>
592+
<framework:place>top_created</framework:place>
593+
<framework:place>finished</framework:place>
594+
595+
<framework:transition name="start">
596+
<framework:from>init</framework:from>
597+
<framework:to weight="4">prepare_leg</framework:to>
598+
<framework:to weight="1">prepare_top</framework:to>
599+
<framework:to weight="1">stopwatch_running</framework:to>
600+
</framework:transition>
601+
<framework:transition name="build_leg">
602+
<framework:from>prepare_leg</framework:from>
603+
<framework:to>leg_created</framework:to>
604+
</framework:transition>
605+
<framework:transition name="build_top">
606+
<framework:from>prepare_top</framework:from>
607+
<framework:to>top_created</framework:to>
608+
</framework:transition>
609+
<framework:transition name="join">
610+
<framework:from weight="4">leg_created</framework:from>
611+
<framework:from>top_created</framework:from>
612+
<framework:from>stopwatch_running</framework:from>
613+
<framework:to>finished</framework:to>
614+
</framework:transition>
615+
</framework:workflow>
616+
</framework:config>
617+
</container>
618+
619+
.. code-block:: php
620+
621+
// config/packages/workflow.php
622+
use App\Entity\TableProject;
623+
use Symfony\Config\FrameworkConfig;
624+
625+
return static function (FrameworkConfig $framework): void {
626+
$makeTable = $framework->workflows()->workflows('make_table');
627+
$makeTable
628+
->type('workflow')
629+
->supports([TableProject::class])
630+
->initialMarking(['init']);
631+
632+
$makeTable->markingStore()
633+
->type('method')
634+
->property('marking');
635+
636+
$makeTable->place()->name('init');
637+
$makeTable->place()->name('prepare_leg');
638+
$makeTable->place()->name('prepare_top');
639+
$makeTable->place()->name('stopwatch_running');
640+
$makeTable->place()->name('leg_created');
641+
$makeTable->place()->name('top_created');
642+
$makeTable->place()->name('finished');
643+
644+
$makeTable->transition()
645+
->name('start')
646+
->from(['init'])
647+
->to([
648+
['place' => 'prepare_leg', 'weight' => 4],
649+
['place' => 'prepare_top', 'weight' => 1],
650+
['place' => 'stopwatch_running', 'weight' => 1],
651+
]);
652+
653+
$makeTable->transition()
654+
->name('build_leg')
655+
->from(['prepare_leg'])
656+
->to(['leg_created']);
657+
658+
$makeTable->transition()
659+
->name('build_top')
660+
->from(['prepare_top'])
661+
->to(['top_created']);
662+
663+
$makeTable->transition()
664+
->name('join')
665+
->from([
666+
['place' => 'leg_created', 'weight' => 4],
667+
'top_created', // weight defaults to 1
668+
'stopwatch_running',
669+
])
670+
->to(['finished']);
671+
};
672+
673+
In this example, when the ``start`` transition is applied, it creates 4 tokens in
674+
the ``prepare_leg`` place, 1 token in ``prepare_top``, and 1 token in
675+
``stopwatch_running``. Then, the ``build_leg`` transition must be applied 4 times
676+
(once for each token), and the ``build_top`` transition once. Finally, the ``join``
677+
transition can only be applied when all 4 legs are created, the top is created,
678+
and the stopwatch is still running.
679+
680+
Weighted transitions can also be defined programmatically using the
681+
:class:`Symfony\\Component\\Workflow\\Arc` class::
682+
683+
use Symfony\Component\Workflow\Arc;
684+
use Symfony\Component\Workflow\Definition;
685+
use Symfony\Component\Workflow\Transition;
686+
use Symfony\Component\Workflow\Workflow;
687+
688+
$definition = new Definition(
689+
['init', 'prepare_leg', 'prepare_top', 'stopwatch_running', 'leg_created', 'top_created', 'finished'],
690+
[
691+
new Transition('start', 'init', [
692+
new Arc('prepare_leg', 4),
693+
new Arc('prepare_top', 1),
694+
'stopwatch_running', // defaults to weight 1
695+
]),
696+
new Transition('build_leg', 'prepare_leg', 'leg_created'),
697+
new Transition('build_top', 'prepare_top', 'top_created'),
698+
new Transition('join', [
699+
new Arc('leg_created', 4),
700+
'top_created',
701+
'stopwatch_running',
702+
], 'finished'),
703+
]
704+
);
705+
706+
$workflow = new Workflow($definition);
707+
$workflow->apply($subject, 'start');
708+
709+
// Build each leg (4 times)
710+
$workflow->apply($subject, 'build_leg');
711+
$workflow->apply($subject, 'build_leg');
712+
$workflow->apply($subject, 'build_leg');
713+
$workflow->apply($subject, 'build_leg');
714+
715+
// Build the top
716+
$workflow->apply($subject, 'build_top');
717+
718+
// Now we can join all parts
719+
$workflow->apply($subject, 'join');
720+
721+
The ``Arc`` class takes two parameters: the place name and the weight (which must be
722+
greater than or equal to 1). When a place is specified as a simple string instead of
723+
an ``Arc`` object, it defaults to a weight of 1.
724+
505725
Using a multiple state marking store
506726
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
507727

0 commit comments

Comments
 (0)