From eddc6a65c673cf3c058a3cc390f06b0267c52cfd Mon Sep 17 00:00:00 2001 From: Kevin van Sonsbeek Date: Tue, 2 Sep 2025 21:00:23 +0200 Subject: [PATCH] [FrameworkBundle] Normalize workflow places separately --- .../DependencyInjection/Configuration.php | 25 +++------ ...lace_follow_by_simplistic_place_config.php | 55 +++++++++++++++++++ ...c_place_follow_by_complex_place_config.php | 55 +++++++++++++++++++ ...lace_follow_by_simplistic_place_config.xml | 49 +++++++++++++++++ ...c_place_follow_by_complex_place_config.xml | 49 +++++++++++++++++ ...lace_follow_by_simplistic_place_config.yml | 37 +++++++++++++ ...c_place_config_with_alternative_syntax.yml | 37 +++++++++++++ ...c_place_follow_by_complex_place_config.yml | 37 +++++++++++++ ...x_place_config_with_alternative_syntax.yml | 37 +++++++++++++ .../FrameworkExtensionTestCase.php | 14 +++++ .../YamlFrameworkExtensionTest.php | 15 +++++ 11 files changed, 392 insertions(+), 18 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_complex_place_follow_by_simplistic_place_config.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_simplistic_place_follow_by_complex_place_config.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_complex_place_follow_by_simplistic_place_config.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_simplistic_place_follow_by_complex_place_config.xml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config_with_alternative_syntax.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config.yml create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config_with_alternative_syntax.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 78ed8b3ac8b6a..eb1f3c43c92e3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -473,27 +473,16 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.'); } - // It's an indexed array of shape ['place1', 'place2'] - if (isset($places[0]) && \is_string($places[0])) { - return array_map(function (string $place) { - return ['name' => $place]; - }, $places); - } - - // It's an indexed array, we let the validation occur - if (isset($places[0]) && \is_array($places[0])) { - return $places; - } - - foreach ($places as $name => $place) { - if (\is_array($place) && \array_key_exists('name', $place)) { - continue; + $normalizedPlaces = []; + foreach ($places as $key => $value) { + if (!\is_array($value)) { + $value = ['name' => $value]; } - $place['name'] = $name; - $places[$name] = $place; + $value['name'] ??= $key; + $normalizedPlaces[] = $value; } - return array_values($places); + return $normalizedPlaces; }) ->end() ->isRequired() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_complex_place_follow_by_simplistic_place_config.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_complex_place_follow_by_simplistic_place_config.php new file mode 100644 index 0000000000000..99e319dc03b65 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_complex_place_follow_by_simplistic_place_config.php @@ -0,0 +1,55 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'workflows' => [ + 'article' => [ + 'type' => 'workflow', + 'supports' => [ + FrameworkExtensionTestCase::class, + ], + 'initial_marking' => ['draft'], + 'metadata' => [ + 'title' => 'article workflow', + 'description' => 'workflow for articles', + ], + 'places' => [ + 'draft' => [ + 'metadata' => [ + 'foo' => 'bar', + ], + ], + 'wait_for_journalist', + 'approved_by_journalist' => [ + 'name' => 'approved_by_journalist', + ], + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ], + 'transitions' => [ + 'request_review' => [ + 'from' => 'draft', + 'to' => ['wait_for_journalist', 'wait_for_spellchecker'], + ], + 'journalist_approval' => [ + 'from' => 'wait_for_journalist', + 'to' => 'approved_by_journalist', + ], + 'spellchecker_approval' => [ + 'from' => 'wait_for_spellchecker', + 'to' => 'approved_by_spellchecker', + ], + 'publish' => [ + 'from' => ['approved_by_journalist', 'approved_by_spellchecker'], + 'to' => 'published', + ], + ], + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_simplistic_place_follow_by_complex_place_config.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_simplistic_place_follow_by_complex_place_config.php new file mode 100644 index 0000000000000..d3579fee196d6 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_simplistic_place_follow_by_complex_place_config.php @@ -0,0 +1,55 @@ +loadFromExtension('framework', [ + 'annotations' => false, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => ['log' => true], + 'workflows' => [ + 'article' => [ + 'type' => 'workflow', + 'supports' => [ + FrameworkExtensionTestCase::class, + ], + 'initial_marking' => ['draft'], + 'metadata' => [ + 'title' => 'article workflow', + 'description' => 'workflow for articles', + ], + 'places' => [ + 'draft', + 'wait_for_journalist' => [ + 'metadata' => [ + 'description' => 'The article is awaiting approval of an authorized journalist.', + ], + ], + 'approved_by_journalist' => [ + 'name' => 'approved_by_journalist', + ], + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ], + 'transitions' => [ + 'request_review' => [ + 'from' => 'draft', + 'to' => ['wait_for_journalist', 'wait_for_spellchecker'], + ], + 'journalist_approval' => [ + 'from' => 'wait_for_journalist', + 'to' => 'approved_by_journalist', + ], + 'spellchecker_approval' => [ + 'from' => 'wait_for_spellchecker', + 'to' => 'approved_by_spellchecker', + ], + 'publish' => [ + 'from' => ['approved_by_journalist', 'approved_by_spellchecker'], + 'to' => 'published', + ], + ], + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_complex_place_follow_by_simplistic_place_config.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_complex_place_follow_by_simplistic_place_config.xml new file mode 100644 index 0000000000000..364faa62feec0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_complex_place_follow_by_simplistic_place_config.xml @@ -0,0 +1,49 @@ + + + + + + + + draft + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + + + bar + + + wait_for_journalist + + wait_for_spellchecker + approved_by_spellchecker + published + + draft + wait_for_journalist + wait_for_spellchecker + + + wait_for_journalist + approved_by_journalist + + + wait_for_spellchecker + approved_by_spellchecker + + + approved_by_journalist + approved_by_spellchecker + published + + + article workflow + workflow for articles + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_simplistic_place_follow_by_complex_place_config.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_simplistic_place_follow_by_complex_place_config.xml new file mode 100644 index 0000000000000..81046a75b969f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_simplistic_place_follow_by_complex_place_config.xml @@ -0,0 +1,49 @@ + + + + + + + + draft + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + draft + + + The article is awaiting approval of an authorized journalist. + + + + wait_for_spellchecker + approved_by_spellchecker + published + + draft + wait_for_journalist + wait_for_spellchecker + + + wait_for_journalist + approved_by_journalist + + + wait_for_spellchecker + approved_by_spellchecker + + + approved_by_journalist + approved_by_spellchecker + published + + + article workflow + workflow for articles + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config.yml new file mode 100644 index 0000000000000..b5e184db55f62 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config.yml @@ -0,0 +1,37 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + workflows: + article: + type: workflow + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + initial_marking: [ draft ] + metadata: + title: article workflow + description: workflow for articles + places: + draft: + metadata: + foo: bar + wait_for_journalist: ~ + approved_by_journalist: ~ + wait_for_spellchecker: ~ + approved_by_spellchecker: ~ + published: ~ + transitions: + request_review: + from: [ draft ] + to: [ wait_for_journalist, wait_for_spellchecker ] + journalist_approval: + from: [ wait_for_journalist ] + to: [ approved_by_journalist ] + spellchecker_approval: + from: [ wait_for_spellchecker ] + to: [ approved_by_spellchecker ] + publish: + from: [ approved_by_journalist, approved_by_spellchecker ] + to: [ published ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config_with_alternative_syntax.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config_with_alternative_syntax.yml new file mode 100644 index 0000000000000..1f3131b9e632f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_complex_place_follow_by_simplistic_place_config_with_alternative_syntax.yml @@ -0,0 +1,37 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + workflows: + article: + type: workflow + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + initial_marking: [ draft ] + metadata: + title: article workflow + description: workflow for articles + places: + - draft: + metadata: + foo: bar + - wait_for_journalist + - approved_by_journalist + - wait_for_spellchecker + - approved_by_spellchecker + - published + transitions: + request_review: + from: [ draft ] + to: [ wait_for_journalist, wait_for_spellchecker ] + journalist_approval: + from: [ wait_for_journalist ] + to: [ approved_by_journalist ] + spellchecker_approval: + from: [ wait_for_spellchecker ] + to: [ approved_by_spellchecker ] + publish: + from: [ approved_by_journalist, approved_by_spellchecker ] + to: [ published ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config.yml new file mode 100644 index 0000000000000..bdd98fd713f08 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config.yml @@ -0,0 +1,37 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + workflows: + article: + type: workflow + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + initial_marking: [ draft ] + metadata: + title: article workflow + description: workflow for articles + places: + draft: ~ + wait_for_journalist: + metadata: + description: The article is awaiting approval of an authorized journalist. + approved_by_journalist: ~ + wait_for_spellchecker: ~ + approved_by_spellchecker: ~ + published: ~ + transitions: + request_review: + from: [ draft ] + to: [ wait_for_journalist, wait_for_spellchecker ] + journalist_approval: + from: [ wait_for_journalist ] + to: [ approved_by_journalist ] + spellchecker_approval: + from: [ wait_for_spellchecker ] + to: [ approved_by_spellchecker ] + publish: + from: [ approved_by_journalist, approved_by_spellchecker ] + to: [ published ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config_with_alternative_syntax.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config_with_alternative_syntax.yml new file mode 100644 index 0000000000000..ca033acb61b49 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_simplistic_place_follow_by_complex_place_config_with_alternative_syntax.yml @@ -0,0 +1,37 @@ +framework: + annotations: false + http_method_override: false + handle_all_throwables: true + php_errors: + log: true + workflows: + article: + type: workflow + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase + initial_marking: [ draft ] + metadata: + title: article workflow + description: workflow for articles + places: + - draft + - wait_for_journalist: + metadata: + description: The article is awaiting approval of an authorized journalist. + - approved_by_journalist + - wait_for_spellchecker + - approved_by_spellchecker + - published + transitions: + request_review: + from: [ draft ] + to: [ wait_for_journalist, wait_for_spellchecker ] + journalist_approval: + from: [ wait_for_journalist ] + to: [ approved_by_journalist ] + spellchecker_approval: + from: [ wait_for_spellchecker ] + to: [ approved_by_spellchecker ] + publish: + from: [ approved_by_journalist, approved_by_spellchecker ] + to: [ published ] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index f34913f8a6b57..ba6ddba3ca71c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -414,6 +414,20 @@ public function testWorkflowShouldHaveOneOfSupportsAndSupportStrategy() $this->createContainerFromFile('workflow_without_support_and_support_strategy'); } + public function testWorkflowWithSimplisticPlaceFollowedByComplexPlace() + { + $container = $this->createContainerFromFile('workflow_with_simplistic_place_follow_by_complex_place_config'); + + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is parsed and registered as a service'); + } + + public function testWorkflowWithComplexPlaceFollowedBySimplisticPlace() + { + $container = $this->createContainerFromFile('workflow_with_complex_place_follow_by_simplistic_place_config'); + + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is parsed and registered as a service'); + } + public function testWorkflowMultipleTransitionsWithSameName() { $container = $this->createContainerFromFile('workflow_with_multiple_transitions_with_same_name'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php index cb5a0a5e16f6f..6caa055dc7f7b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -22,4 +23,18 @@ protected function loadFromFile(ContainerBuilder $container, $file) $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); $loader->load($file.'.yml'); } + + public function testWorkflowWithSimplisticPlaceFollowedByComplexPlaceWithAlternativeSyntax() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Unrecognized option "wait_for_journalist" under "framework.workflows.workflows.article.places.1". Available options are "metadata", "name".'); + $this->createContainerFromFile('workflow_with_simplistic_place_follow_by_complex_place_config_with_alternative_syntax'); + } + + public function testWorkflowWithComplexPlaceFollowedBySimplisticPlaceWithAlternativeSyntax() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Unrecognized option "draft" under "framework.workflows.workflows.article.places.0". Available options are "metadata", "name".'); + $this->createContainerFromFile('workflow_with_complex_place_follow_by_simplistic_place_config_with_alternative_syntax'); + } }