{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/11882","repository_url":"https://api.github.com/repos/symfony/symfony","labels_url":"https://api.github.com/repos/symfony/symfony/issues/11882/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/11882/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/11882/events","html_url":"https://github.com/symfony/symfony/pull/11882","id":42218302,"number":11882,"title":"[Workflow] Introducing the workflow component","user":{"login":"lyrixx","id":408368,"avatar_url":"https://avatars.githubusercontent.com/u/408368?v=3","gravatar_id":"","url":"https://api.github.com/users/lyrixx","html_url":"https://github.com/lyrixx","followers_url":"https://api.github.com/users/lyrixx/followers","following_url":"https://api.github.com/users/lyrixx/following{/other_user}","gists_url":"https://api.github.com/users/lyrixx/gists{/gist_id}","starred_url":"https://api.github.com/users/lyrixx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lyrixx/subscriptions","organizations_url":"https://api.github.com/users/lyrixx/orgs","repos_url":"https://api.github.com/users/lyrixx/repos","events_url":"https://api.github.com/users/lyrixx/events{/privacy}","received_events_url":"https://api.github.com/users/lyrixx/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/symfony/symfony/labels/Feature","name":"Feature","color":"02e10c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":57,"created_at":"2014-09-08T16:55:24Z","updated_at":"2016-04-26T13:17:25Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/11882","html_url":"https://github.com/symfony/symfony/pull/11882","diff_url":"https://github.com/symfony/symfony/pull/11882.diff","patch_url":"https://github.com/symfony/symfony/pull/11882.patch"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Bug fix?      | no\r\n| New feature?  | yes\r\n| BC breaks?    | no\r\n| Deprecations? | no\r\n| Tests pass?   | not yet\r\n| Fixed tickets | n/a\r\n| License       | MIT\r\n| Doc PR        | n/a\r\n\r\nTODO:\r\n\r\n* [x] Add tests\r\n* [x] Add PHP doc\r\n* [x] Add Symfony fullstack integration (Config, DIC, command to dump the state-machine into graphiz format)\r\n\r\nSo why another component?\r\n\r\nThis component take another approach that what you can find on [Packagist](https://packagist.org/search/?q=state%20machine).\r\n\r\nHere, the workflow component is not tied to a specific object like with [Finite](https://github.com/yohang/Finite). It means that the component workflow is stateless and can be a symfony service.\r\n\r\nSome code:\r\n\r\n```php\r\n#!/usr/bin/env php\r\n<?php\r\n\r\nrequire __DIR__.'/vendor/autoload.php';\r\n\r\nuse Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher;\r\nuse Symfony\\Component\\EventDispatcher\\EventDispatcher;\r\nuse Symfony\\Component\\Stopwatch\\Stopwatch;\r\nuse Symfony\\Component\\Workflow\\Definition;\r\nuse Symfony\\Component\\Workflow\\Dumper\\GraphvizDumper;\r\nuse Symfony\\Component\\Workflow\\Marking;\r\nuse Symfony\\Component\\Workflow\\MarkingStore\\PropertyAccessorMarkingStore;\r\nuse Symfony\\Component\\Workflow\\MarkingStore\\ScalarMarkingStore;\r\nuse Symfony\\Component\\Workflow\\Transition;\r\nuse Symfony\\Component\\Workflow\\Workflow;\r\n\r\nclass Foo\r\n{\r\n    public $marking;\r\n\r\n    public function __construct($init = 'a')\r\n    {\r\n        $this->marking = $init;\r\n        $this->marking = [$init => true];\r\n    }\r\n}\r\n\r\n$fooDefinition = new Definition(Foo::class);\r\n$fooDefinition->addPlaces([\r\n    'a', 'b', 'c', 'd', 'e', 'f', 'g',\r\n]);\r\n\r\n//                                           name  from        to\r\n$fooDefinition->addTransition(new Transition('t1', 'a',        ['b', 'c']));\r\n$fooDefinition->addTransition(new Transition('t2', ['b', 'c'],  'd'));\r\n$fooDefinition->addTransition(new Transition('t3', 'd',         'e'));\r\n$fooDefinition->addTransition(new Transition('t4', 'd',         'f'));\r\n$fooDefinition->addTransition(new Transition('t5', 'e',         'g'));\r\n$fooDefinition->addTransition(new Transition('t6', 'f',         'g'));\r\n\r\n$graph = (new GraphvizDumper())->dump($fooDefinition);\r\n\r\n$ed = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch(), new \\Monolog\\Logger('app'));\r\n\r\n// $workflow = new Workflow($fooDefinition, new ScalarMarkingStore(), $ed);\r\n$workflow = new Workflow($fooDefinition, new PropertyAccessorMarkingStore(), $ed);\r\n\r\n$foo = new Foo(isset($argv[1]) ? $argv[1] : 'a');\r\n\r\n$graph = (new GraphvizDumper())->dump($fooDefinition, $workflow->getMarking($foo));\r\n\r\ndump([\r\n    'AvailableTransitions' => $workflow->getAvailableTransitions($foo),\r\n    'CurrentMarking' => clone $workflow->getMarking($foo),\r\n    'can validate t1' => $workflow->can($foo, 't1'),\r\n    'can validate t3' => $workflow->can($foo, 't3'),\r\n    'can validate t6' => $workflow->can($foo, 't6'),\r\n    'apply t1' => clone $workflow->apply($foo, 't1'),\r\n    'can validate t2' => $workflow->can($foo, 't2'),\r\n    'apply t2' => clone $workflow->apply($foo, 't2'),\r\n    'can validate t1 bis' => $workflow->can($foo, 't1'),\r\n    'can validate t3 bis' => $workflow->can($foo, 't3'),\r\n    'can validate t6 bis' => $workflow->can($foo, 't6'),\r\n]);\r\n\r\n```\r\n\r\nThe workflown:\r\n\r\n![workflow](https://cloud.githubusercontent.com/assets/408368/14183999/4a43483c-f773-11e5-9c8b-7f157e0cb75f.png)\r\n\r\nThe output:\r\n\r\n```\r\narray:10 [\r\n  \"AvailableTransitions\" => array:1 [\r\n    0 => Symfony\\Component\\Workflow\\Transition {#4\r\n      -name: \"t1\"\r\n      -froms: array:1 [\r\n        0 => \"a\"\r\n      ]\r\n      -tos: array:2 [\r\n        0 => \"b\"\r\n        1 => \"c\"\r\n      ]\r\n    }\r\n  ]\r\n  \"CurrentMarking\" => Symfony\\Component\\Workflow\\Marking {#19\r\n    -places: array:1 [\r\n      \"a\" => true\r\n    ]\r\n  }\r\n  \"can validate t1\" => true\r\n  \"can validate t3\" => false\r\n  \"can validate t6\" => false\r\n  \"apply t1\" => Symfony\\Component\\Workflow\\Marking {#22\r\n    -places: array:2 [\r\n      \"b\" => true\r\n      \"c\" => true\r\n    ]\r\n  }\r\n  \"apply t2\" => Symfony\\Component\\Workflow\\Marking {#47\r\n    -places: array:1 [\r\n      \"d\" => true\r\n    ]\r\n  }\r\n  \"can validate t1 bis\" => false\r\n  \"can validate t3 bis\" => true\r\n  \"can validate t6 bis\" => false\r\n]\r\n```"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/214738380","html_url":"https://github.com/symfony/symfony/pull/11882#issuecomment-214738380","issue_url":"https://api.github.com/repos/symfony/symfony/issues/11882","id":214738380,"user":{"login":"mikeSimonson","id":907613,"avatar_url":"https://avatars.githubusercontent.com/u/907613?v=3","gravatar_id":"","url":"https://api.github.com/users/mikeSimonson","html_url":"https://github.com/mikeSimonson","followers_url":"https://api.github.com/users/mikeSimonson/followers","following_url":"https://api.github.com/users/mikeSimonson/following{/other_user}","gists_url":"https://api.github.com/users/mikeSimonson/gists{/gist_id}","starred_url":"https://api.github.com/users/mikeSimonson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikeSimonson/subscriptions","organizations_url":"https://api.github.com/users/mikeSimonson/orgs","repos_url":"https://api.github.com/users/mikeSimonson/repos","events_url":"https://api.github.com/users/mikeSimonson/events{/privacy}","received_events_url":"https://api.github.com/users/mikeSimonson/received_events","type":"User","site_admin":false},"created_at":"2016-04-26T13:17:25Z","updated_at":"2016-04-26T13:17:25Z","body":"Hi @lyrixx,\r\n\r\nFirst thank you very much for that nice component.\r\nBefore it goes gold, I have a few suggestion that I'd like your input on and that I would happily implement.\r\n\r\nBy toying a little bit with the code, I realised that some classes like Definition and Transition were lacking some doc blocks and while I was adding those I noticed that the Places are not an object but only a string.\r\n\r\nThis seemed odd to me and I wanted to know what you though about making Place an object and then adding those docblocks ?\r\n\r\nRegards"}}