-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Added docs for Workflow component #6871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
d0701f0
91867c2
69bca59
f99fbb2
e797f0c
5b2a029
196baf9
763950d
cead2f7
e0089c5
48de43d
805b237
f57ec14
03925ff
e6bdee6
c7464c7
83d26c1
6e7a35f
4415466
866b25a
dceebec
b959f8a
b45edf2
fefdb5f
7f0f5b0
c681283
86ecf0a
d002a8b
b0a8855
4e7cf11
3aa433d
4f277dc
2511c21
c9b1656
2cc2934
47dc11d
3250621
c0bd6da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,8 @@ | |
The Workflow Component | ||
====================== | ||
|
||
The Workflow component provides tools for managing a workflow or finite state | ||
machine. | ||
The Workflow component provides tools for managing a workflow or finite | ||
state machine. | ||
|
||
.. versionadded:: 3.2 | ||
The Workflow component was introduced in Symfony 3.2. | ||
|
@@ -19,6 +19,75 @@ You can install the component in 2 different ways: | |
* :doc:`Install it via Composer </components/using_components>` (``symfony/workflow`` on `Packagist`_); | ||
* Use the official Git repository (https://github.com/symfony/workflow). | ||
|
||
For more information, see the code in the Git Repository. | ||
.. include:: /components/require_autoload.rst.inc | ||
|
||
Creating a Workflow | ||
------------------- | ||
|
||
The workflow component gives you an object oriented way to define a process | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The more I read this, the more I think we should rename - at least in docs - |
||
or a life cycle that your object goes through. Each step or stage in the | ||
process is called a *place*. You do also define *transitions* to that describes | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
the action to get from one place to another. | ||
|
||
.. image:: /_images/components/workflow/states_transitions.png | ||
|
||
A set of places and transitions creates a **definition**. A workflow needs | ||
a ``Definition`` and a way to write the states to the objects, (i.e. an | ||
instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing closing parenthesis at the end of this line. |
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should also the initial place with something like: |
||
Consider the following example for a blog post. A post can have places: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ->
|
||
'draft', 'review', 'rejected', 'published'. You can define the workflow | ||
like this:: | ||
|
||
use Symfony\Component\Workflow\Definition; | ||
use Symfony\Component\Workflow\Transition; | ||
use Symfony\Component\Workflow\Workflow; | ||
use Symfony\Component\Workflow\MarkingStore\ScalarMarkingStore; | ||
|
||
$states = ['draft', 'review', 'rejected', 'published']; | ||
|
||
// Define a transaction with a name, where to go from and where to go to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I'd use workflow terminology:
|
||
$transitions[] = new Transition('to_review', 'draft', 'review'); | ||
$transitions[] = new Transition('publish', 'review', 'published'); | ||
$transitions[] = new Transition('reject', 'review', 'rejected'); | ||
|
||
$definition = new Definition($states, $transitions); | ||
$definition->setInitialPlace('draft'); | ||
|
||
$marking = new ScalarMarkingStore('currentState'); | ||
$workflow = new Workflow($definition, $marking); | ||
|
||
The ``Workflow`` can now help you to decide what actions that are allowed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
on a blog post depending on what *place* it is in. This will keep your domain | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not about domain logic, it's more about Workflow management logic, isn'it ? I'd say instead "This will keep your Workflow logic in one place and not spread all over your applications.", what do you think ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. I would always consider this the domain logic because the "workflow management logic" is a part of your domain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The theory behind "Workflow management" is to put the Workflow management outside of your domain, if I've well understood. BlogPost entity shouldn't be modified every time the rules of publishing evolve, for instance. I'm pretty noob of this field, so correct me if I'm wrong :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Correct, you just edit the workflow when the rules of publishing changes.
Outside the domain or outside your model? The way I see it there is only three places you can put code/config:
Can @lyrixx give some input? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure to understand the question / debate here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I can't do it, because the primary purpose of this Workflow component is not obvious for me:
The component is designed as very generic thing, so in theory it probably may have some interesting applications. However, provided examples don't impress. Compare it with rich domain model: final class Article
{
private $id;
private $title;
private $approvedByJournalist;
private $approvedBySpellchecker;
private $published;
public static function postDraft(int $articleId, ArticleTitle $title): Article
{
$draft = new self();
$draft->apply(new DraftArticleWasPosted($articleId, $title));
$draft->apply(new ArticleWasSentForApproval($this->id));
return $draft;
}
private function __construct() {}
public function approveByJournalist()
{
if ($this->approvedByJournalist) {
return;
}
$this->apply(new ArticleWasApprovedByJournalist($this->id));
$this->publishIfItIsReady();
}
public function approveBySpellchecker()
{
if ($this->approvedBySpellchecker) {
return;
}
$this->apply(new ArticleWasApprovedBySpellchecker($this->id));
$this->publishIfItIsReady();
}
public function changeTitle(ArticleTitle $newTitle)
{
if ($this->title->equals($newTitle)) {
return;
}
$this->apply(new ArticleTitleWasChanged($this->id, $newTitle));
$this->apply(new ArticleWasSentForApproval($this->id));
}
private function publishIfItIsReady()
{
if ($this->approvedByJournalist && $this->approvedBySpellchecker) {
$this->apply(new ArticleWasPublished($this->id));
}
}
// ...
private function onArticleWasApprovedByJournalist(ArticleWasApprovedByJournalist $event)
{
$this->approvedByJournalist = true;
}
private function onArticleWasSentForApproval(ArticleWasSentForApproval $event)
{
$this->approvedByJournalist = false;
$this->approvedBySpellchecker = false;
$this->published = false;
}
private function onArticleTitleWasChanged(ArticleTitleWasChanged $event)
{
$this->title = $event->getNewTitle();
}
} We see use-cases ( Further, that sample project checks permissions with public function onTransitionJournalist(GuardEvent $event)
{
if (!$this->checker->isGranted('ROLE_JOURNALIST')) {
$event->setBlocked(true);
}
} ... and how you can solve it with commands: final class ApproveArticleByJournalistHandler
{
/**
* @acl_role ROLE_JOURNALIST
*/
public function handle(ApproveArticleByJournalist $command)
{
$blogPost = $this->blogPostRepository->find($command->getArticleId());
$blogPost->approveByJournalist();
$this->blogPostRepository->save($blogPost);
}
} And again we lose expressiveness by generic I would like to know if I'm wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO, all this debate is totally out of the scope of this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is it possible to write docs for the component without clear applications, examples? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not said that. But you you are telling us the Workflow component is useless and it's better to write code like your example. It's really a matter of taste (I have a strong opinion on that ; but I let people making their own choices). So I'm juste saying: The component is here, and it's useless to discuss if it's better to use CQRS / ES over Workflow Component. For the record, the discussion is about the following sentence:
IMHO, it's more important to document how the component works. I like the @Nyholm's work ; so let's focus on what matter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @unkind: I hear you. Your arguments are valid. This is, as Grégoire says, out of scope for this PR. @unkind, I've asked you to create an issue on the symfony/symfony repository. I am happy to make a PR to try to improve the Workflow component regarding your input. Let's work together on this, but not in this PR. |
||
logic in one place and not spread all over your application. | ||
|
||
When you start defining multiple workflows you should consider putting them | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
-->
|
||
in a ``Registry``. A registry will also help you to decide if a workflow | ||
supports the object you are trying to use it with:: | ||
|
||
use Symfony\Component\Workflow\Registry; | ||
use Acme\Entity\BlogPost; | ||
use Acme\Entity\Newsletter; | ||
|
||
$blogWorkflow = ... | ||
$newsletterWorkflow = ... | ||
|
||
$registry = new Registry(); | ||
$registry->add($blogWorkflow, BlogPost::class); | ||
$registry->add($newsletterWorkflow, Newsletter::class); | ||
|
||
// ... | ||
$post = new BlogPost(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please prefix the example with // ...
$post = new BlogPost(); |
||
$workflow = $registry->get($post); | ||
|
||
Learn more | ||
---------- | ||
|
||
.. toctree:: | ||
:maxdepth: 1 | ||
:glob: | ||
|
||
/workflow | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this one. It kinda makes sense, but it'll ruin Sphinx's global toctree afaics. |
||
/workflow/* | ||
|
||
.. _Packagist: https://packagist.org/packages/symfony/workflow |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
Workflow | ||
-------- | ||
|
||
A workflow is a model of a process in your application. It may be the process | ||
of how a blog post goes from draft, review and publish. An other example is when | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
a user submitts a series of different forms to complete a task. Such process are | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
best kept away from your models and should be defined in configuration. | ||
|
||
A state machine is a subset of a workflow and its purpose is to hold a state of | ||
your model. Both the workflow and state machine defines what actions (transitions) | ||
that are allowed on the model at each state. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. two blank lines |
||
.. toctree:: | ||
:maxdepth: 1 | ||
:glob: | ||
|
||
workflow/* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.. index:: | ||
single: Workflow; Dumping Workflows | ||
|
||
How to Dump Workflows | ||
===================== | ||
|
||
To help you debug you could dump a representation of your workflow with | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
the use of a ``DumperInterface``. Use the ``GraphvizDumper`` to create a | ||
PNG image of the workflow defined above:: | ||
|
||
// dump-graph.php | ||
$dumper = new GraphvizDumper(); | ||
echo $dumper->dump($definition); | ||
|
||
.. code-block:: bash | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We recently switched to "terminal" for terminal examples. |
||
|
||
$ php dump-graph-php > out.dot | ||
$ dot -Tpng out.dot -o graph.png | ||
|
||
The result will look like this: | ||
|
||
.. image:: /_images/components/workflow/blogpost.png | ||
|
||
.. note:: | ||
|
||
The ``dot`` command is a part of Graphviz. You can download it and read | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is part of |
||
more about it on `Graphviz.org`_. | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove the double empty line |
||
.. _Graphviz.org: http://www.graphviz.org |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
.. index:: | ||
single: Workflow; Workflows as State Machines | ||
|
||
Workflows as State Machines | ||
=========================== | ||
|
||
The workflow component is modelled after a *Workflow net* which is a subclass | ||
of a `Petri net`_. By adding further restrictions you can get a state machine. | ||
The most important one being that a state machine cannot be in more than | ||
one place simultaneously. It is also worth noting that a workflow does not | ||
commonly have cyclic path in the definition graph but it is common for a state | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "[...] definition graph, but it [...]" |
||
machine. | ||
|
||
.. configuration-block:: | ||
|
||
.. code-block:: yaml | ||
|
||
framework: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add |
||
workflows: | ||
blog_publishing: | ||
type: | ||
type: 'state_machine' | ||
supports: | ||
- AppBundle\Entity\BlogPost | ||
places: | ||
- draft | ||
- review | ||
- rejected | ||
- published | ||
transitions: | ||
to_review: | ||
from: [draft, rejected] | ||
to: review | ||
publish: | ||
from: review | ||
to: published | ||
reject: | ||
from: review | ||
to: rejected | ||
|
||
|
||
With the configuration above we allow an object in place ``draft`` **or** | ||
``rejected`` to be moved to ``review``. If the marking store had been of | ||
type ``scalar`` the object had to be in **both** places. | ||
|
||
.. code-block:: php | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of this directive -> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless the previous paragraph didn't end with a colon (which is the case here). Although reSt allows us to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wouterj another reason to stop using this ugly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think |
||
|
||
$workflow = $this->container->get('state_machine.blog_publishing'); | ||
$post = new \BlogPost(); | ||
|
||
$post->state = 'draft'; | ||
$workflow->can($post, 'to_review'); // True | ||
|
||
$post->state = 'rejected'; | ||
$workflow->can($post, 'to_review'); // True | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These extra blank lines can be removed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. multiple blank lines should always be just one |
||
|
||
|
||
|
||
.. _Petri net: https://en.wikipedia.org/wiki/Petri_net |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
.. index:: | ||
single: Workflow; Usage | ||
|
||
How to Use the Workflow | ||
======================= | ||
|
||
Using the workflow component will help you to keep your domain logic as | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Workflow component |
||
configuration. Having domain logic in one place gives you a better overview | ||
and it is easier to maintain whenever the domain requirement changes since | ||
you do not have to edit all your controllers, twig templates and services. | ||
|
||
A workflow is a process or a lifecycle that your objects go through. Each | ||
step or stage in the process is called a *place*. You do also define *transitions* | ||
to that describes the action to get from one place to another. | ||
|
||
.. image:: /_images/components/workflow/states_transitions.png | ||
|
||
A set of places and transitions creates a **definition**. A workflow needs | ||
a ``Definition`` and a way to write the states to the objects, (i.e. an | ||
instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing closing parenthesis at the end of this line. |
||
|
||
Consider the following example for a blog post. A post can have places: | ||
'draft', 'review', 'rejected', 'published'. You can define the workflow | ||
like this: | ||
|
||
.. configuration-block:: | ||
|
||
.. code-block:: yaml | ||
|
||
framework: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add the file comment + XML and PHP here as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Thank you |
||
workflows: | ||
blog_publishing: | ||
type: 'workflow' # or 'state_machine' | ||
marking_store: | ||
type: 'property_accessor' # or 'scalar' or 'state_machine' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to have the configuration references as well. Is the initial state automatically guessed from the first place in the list ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW, the type of marking store can not be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Fixed now. |
||
arguments: | ||
- 'currentPlace' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about arguments:
- 'status' # The name of property that holds the marking in the object |
||
supports: | ||
- AppBundle\Entity\BlogPost | ||
places: | ||
- draft | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - drafted # By default the initial state is the first place
- to_review while renaming the transition 'submit' ? |
||
- review | ||
- rejected | ||
- published | ||
transitions: | ||
to_review: | ||
from: draft | ||
to: review | ||
publish: | ||
from: review | ||
to: published | ||
reject: | ||
from: review | ||
to: rejected | ||
|
||
|
||
.. code-block: php | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: should be a double colon |
||
|
||
class BlogPost | ||
{ | ||
// This property is used by the marking store | ||
public $currentPlace; | ||
public $title; | ||
public $content | ||
} | ||
|
||
.. note:: | ||
|
||
The marking store type could be "property_accessor" or "scalar". | ||
A scalar marking type does not support a model being on multiple places. | ||
|
||
With this workflow named ``blog_publishing`` you can get help to decide | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "[...] named |
||
what actions that are allowed on a blog post. | ||
|
||
.. code-block:: php | ||
|
||
$post = new \BlogPost(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't this be |
||
|
||
$workflow = $this->container->get('workflow.blog_publishing'); | ||
$workflow->can($post, 'publish'); // False | ||
$workflow->can($post, 'to_review'); // True | ||
|
||
// Update the currentState on the post | ||
try { | ||
$workflow->apply($post, 'to_review'); | ||
} catch (LogicException $e) { | ||
// ... | ||
} | ||
|
||
// See all the available transition for the post in the current state | ||
$transitions = $workflow->getEnabledTransitions($post); | ||
|
||
Using Events | ||
------------ | ||
|
||
To make your workflows even more powerful you could construct the ``Workflow`` | ||
object with an ``EventDispatcher``. You can now create event listeners to | ||
block transitions ie depending on the data in the blog post. The following | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "block transitions (i.e. depending on the data in the blog post)." |
||
events are dispatched: | ||
|
||
* ``workflow.guard`` | ||
* ``workflow.[workflow name].guard`` | ||
* ``workflow.[workflow name].guard.[transition name]`` | ||
|
||
See example to make sure no blog post without title is moved to "review":: | ||
|
||
class BlogPostReviewListener implements EventSubscriberInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing use statements (for the |
||
{ | ||
public function guardReview(GuardEvent $event) | ||
{ | ||
/** @var Acme\BlogPost $post */ | ||
$post = $event->getSubject(); | ||
$title = $post->title; | ||
|
||
if (empty($title)) { | ||
// Posts with no title should not be allowed | ||
$event->setBlocked(true); | ||
} | ||
} | ||
|
||
public static function getSubscribedEvents() | ||
{ | ||
return array( | ||
'workflow.blogpost.guard.to_review' => array('guardReview'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method does not need to be in an array. |
||
); | ||
} | ||
} | ||
|
||
With help from the ``EventDispatcher`` and the ``AuditTrailListener`` you | ||
could easily enable logging:: | ||
|
||
$logger = new PSR3Logger(); | ||
$subscriber = new AuditTrailListener($logger); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing use statements |
||
$dispatcher->addSubscriber($subscriber); | ||
|
||
Usage in Twig | ||
------------- | ||
|
||
Using your workflow in your Twig templates reduces the need of domain logic | ||
in the view layer. Consider this example of the control panel for our blog's | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "[...] for our blog's edit page" -> "[...] of the blog." ? (again, to remove first person) |
||
edit page. The links below will only be displayed when the action is allowed: | ||
|
||
.. code-block:: twig | ||
|
||
<h3>Actions</h3> | ||
{% if workflow_can(post, 'publish') %} | ||
<a href="...">Publish article</a> | ||
{% endif %} | ||
{% if workflow_can(post, 'to_review') %} | ||
<a href="...">Submit to review</a> | ||
{% endif %} | ||
{% if workflow_can(post, 'reject') %} | ||
<a href="...">Reject article</a> | ||
{% endif %} | ||
|
||
{# Or loop through the enabled transistions #} | ||
{% for transition in workflow_transitions(article) %} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! I've updated the PR |
||
<a href="...">{{ transition.name }}</a> | ||
{% else %} | ||
No actions available. | ||
{% endfor %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't a Petri net instead ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mickaelandrieu a workflow is a subset of a petri net (and a state machine a subset of workflow). The component does not support all features necessary to implement a petri net
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's unclear to me right now, but it's not a problem in the docs: I just need to learn a little bit more about all of theses implementations ^^
Thank you @stof