Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4d58fa7

Browse files
feature #60204 [FrameworkBundle] Add support for configuring workflow places with glob patterns matching consts/backed enums (lyrixx)
This PR was merged into the 7.4 branch. Discussion ---------- [FrameworkBundle] Add support for configuring workflow places with glob patterns matching consts/backed enums | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | | License | MIT This PR improves the DX on two aspects: - allowing to use FWCN::glob patterns to list places - works with enums and consts - allowing to use backed enums to list places and reference them in transitions --- #### Example with consts: ```yaml framework: workflows: my_workflow_name: places: 'App\Workflow\MyWorkflow::PLACE_*' transitions: one: from: !php/const App\Workflow\MyWorkflow::PLACE_A to: !php/const App\Workflow\MyWorkflow::PLACE_B ``` and ```php <?php namespace App\Workflow; class MyWorkflow { const PLACE_A = 'a'; const PLACE_B = 'b'; const PLACE_C = 'c'; // [...] } ``` --- #### Example with enums: ```yaml framework: workflows: my_workflow_name: places: !php/enum App\Workflow\Places transitions: one: from: !php/enum App\Workflow\Places::A to: !php/enum App\Workflow\Places::B ``` and ```php <?php namespace App\Workflow; enum Places: string { case A = 'a'; case B = 'b'; case C = 'c'; } ``` Commits ------- 72dd914 [FrameworkBundle] Add support for configuring workflow places with glob patterns matching consts/backed enums
2 parents 3ad43c9 + 72dd914 commit 4d58fa7

File tree

10 files changed

+128
-26
lines changed

10 files changed

+128
-26
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CHANGELOG
1212
* Add `framework.type_info.aliases` option
1313
* Add `KernelBrowser::getSession()`
1414
* Add autoconfiguration tag `kernel.uri_signer` to `Symfony\Component\HttpFoundation\UriSigner`
15+
* Add support for configuring workflow places with glob patterns matching consts/backed enums
1516

1617
7.3
1718
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
2727
use Symfony\Component\DependencyInjection\ContainerBuilder;
2828
use Symfony\Component\DependencyInjection\Exception\LogicException;
29+
use Symfony\Component\Finder\Glob;
2930
use Symfony\Component\Form\Form;
3031
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
3132
use Symfony\Component\HttpClient\HttpClient;
@@ -364,7 +365,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
364365
->arrayNode('workflows', 'workflow')
365366
->canBeEnabled()
366367
->beforeNormalization()
367-
->always(function ($v) {
368+
->always(static function ($v) {
368369
if (\is_array($v) && true === $v['enabled']) {
369370
$workflows = $v;
370371
unset($workflows['enabled']);
@@ -478,15 +479,36 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
478479
->end()
479480
->arrayNode('places', 'place')
480481
->beforeNormalization()
481-
->always()
482-
->then(static function ($places) {
483-
if (!\is_array($places)) {
484-
throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.');
482+
->always(static function ($places) {
483+
if (\is_string($places)) {
484+
if (2 !== \count($places = explode('::', $places, 2))) {
485+
throw new InvalidConfigurationException('The "places" option must be a "FQCN::glob" pattern in workflow configuration.');
486+
}
487+
[$class, $pattern] = $places;
488+
if (!class_exists($class) && !interface_exists($class, false)) {
489+
throw new InvalidConfigurationException(\sprintf('The "places" option must be a "FQCN::glob" pattern in workflow configuration, but class "%s" is not found.', $class));
490+
}
491+
492+
$places = [];
493+
$regex = Glob::toRegex($pattern, false);
494+
495+
foreach ((new \ReflectionClass($class))->getConstants() as $name => $value) {
496+
if (preg_match($regex, $name)) {
497+
$places[] = $value;
498+
}
499+
}
500+
if (!$places) {
501+
throw new InvalidConfigurationException(\sprintf('No places found for pattern "%s::%s" in workflow configuration.', $class, $pattern));
502+
}
503+
} elseif (!\is_array($places)) {
504+
throw new InvalidConfigurationException('The "places" option must be an array or a "FQCN::glob" pattern in workflow configuration.');
485505
}
486506

487507
$normalizedPlaces = [];
488508
foreach ($places as $key => $value) {
489-
if (!\is_array($value)) {
509+
if ($value instanceof \BackedEnum) {
510+
$value = ['name' => $value->value];
511+
} elseif (!\is_array($value)) {
490512
$value = ['name' => $value];
491513
}
492514
$value['name'] ??= $key;
@@ -514,8 +536,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
514536
->end()
515537
->arrayNode('transitions', 'transition')
516538
->beforeNormalization()
517-
->always()
518-
->then(static function ($transitions) {
539+
->always(static function ($transitions) {
519540
if (!\is_array($transitions)) {
520541
throw new InvalidConfigurationException('The "transitions" option must be an array in workflow configuration.');
521542
}
@@ -550,14 +571,18 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
550571
->example('is_fully_authenticated() and is_granted(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'')
551572
->end()
552573
->arrayNode('from')
553-
->beforeNormalization()->castToArray()->end()
574+
->beforeNormalization()
575+
->always(static fn ($from) => array_map(static fn ($v) => $v instanceof \BackedEnum ? $v->value : $v, \is_array($from) ? $from : [$from]))
576+
->end()
554577
->requiresAtLeastOneElement()
555578
->prototype('scalar')
556579
->cannotBeEmpty()
557580
->end()
558581
->end()
559582
->arrayNode('to')
560-
->beforeNormalization()->castToArray()->end()
583+
->beforeNormalization()
584+
->always(static fn ($to) => array_map(static fn ($v) => $v instanceof \BackedEnum ? $v->value : $v, \is_array($to) ? $to : [$to]))
585+
->end()
561586
->requiresAtLeastOneElement()
562587
->prototype('scalar')
563588
->cannotBeEmpty()
@@ -582,28 +607,23 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
582607
->end()
583608
->end()
584609
->validate()
585-
->ifTrue(static function ($v) {
586-
return $v['supports'] && isset($v['support_strategy']);
587-
})
610+
->ifTrue(static fn ($v) => $v['supports'] && isset($v['support_strategy']))
588611
->thenInvalid('"supports" and "support_strategy" cannot be used together.')
589612
->end()
590613
->validate()
591-
->ifTrue(static function ($v) {
592-
return !$v['supports'] && !isset($v['support_strategy']);
593-
})
614+
->ifTrue(static fn ($v) => !$v['supports'] && !isset($v['support_strategy']))
594615
->thenInvalid('"supports" or "support_strategy" should be configured.')
595616
->end()
596617
->beforeNormalization()
597-
->always()
598-
->then(static function ($values) {
599-
// Special case to deal with XML when the user wants an empty array
600-
if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) {
601-
$values['events_to_dispatch'] = [];
602-
unset($values['event_to_dispatch']);
603-
}
618+
->always(static function ($values) {
619+
// Special case to deal with XML when the user wants an empty array
620+
if (\array_key_exists('event_to_dispatch', $values) && null === $values['event_to_dispatch']) {
621+
$values['events_to_dispatch'] = [];
622+
unset($values['event_to_dispatch']);
623+
}
604624

605-
return $values;
606-
})
625+
return $values;
626+
})
607627
->end()
608628
->end()
609629
->end()

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@
475475
<xsd:attribute name="initial-marking" type="xsd:string" />
476476
<xsd:attribute name="support-strategy" type="xsd:string" />
477477
<xsd:attribute name="enabled" type="xsd:boolean" />
478+
<xsd:attribute name="places" type="xsd:string" />
478479
</xsd:complexType>
479480

480481
<xsd:complexType name="php-errors">
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow;
4+
5+
enum Places: string
6+
{
7+
case A = 'a';
8+
case B = 'b';
9+
case C = 'c';
10+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places;
4+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase;
5+
6+
$container->loadFromExtension('framework', [
7+
'workflows' => [
8+
'enum' => [
9+
'supports' => [
10+
FrameworkExtensionTestCase::class,
11+
],
12+
'places' => Places::cases(),
13+
'transitions' => [
14+
'one' => [
15+
'from' => Places::A->value,
16+
'to' => Places::B->value,
17+
],
18+
'two' => [
19+
'from' => Places::B->value,
20+
'to' => Places::C->value,
21+
],
22+
],
23+
]
24+
],
25+
]);

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places;
34
use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase;
45

56
$container->loadFromExtension('framework', [
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:framework="http://symfony.com/schema/dic/symfony"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
8+
9+
<framework:config http-method-override="false" handle-all-throwables="true">
10+
<framework:workflow name="enum" type="state_machine" places="Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::*">
11+
<framework:marking-store service="workflow_service"/>
12+
<framework:support>Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase</framework:support>
13+
<framework:transition name="one">
14+
<framework:from>a</framework:from>
15+
<framework:to>b</framework:to>
16+
</framework:transition>
17+
<framework:transition name="two">
18+
<framework:from>b</framework:from>
19+
<framework:to>c</framework:to>
20+
</framework:transition>
21+
</framework:workflow>
22+
</framework:config>
23+
</container>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
framework:
2+
workflows:
3+
enum:
4+
supports:
5+
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase
6+
places: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places
7+
transitions:
8+
one:
9+
from: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::A
10+
to: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::B
11+
two:
12+
from: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::B
13+
to: !php/enum Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Places::C

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,14 @@ public function testWorkflowMultipleTransitionsWithSameName()
519519
], $container->getDefinition($transitions[4])->getArguments());
520520
}
521521

522+
public function testWorkflowEnum()
523+
{
524+
$container = $this->createContainerFromFile('workflow_enum');
525+
526+
$workflowDefinition = $container->getDefinition('state_machine.enum.definition');
527+
$this->assertSame(['a', 'b', 'c'], $workflowDefinition->getArgument(0));
528+
}
529+
522530
public function testWorkflowGuardExpressions()
523531
{
524532
$container = $this->createContainerFromFile('workflow_with_guard_expression');

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function testAssetPackageCannotHavePathAndUrl()
7575
public function testWorkflowValidationPlacesIsArray()
7676
{
7777
$this->expectException(InvalidConfigurationException::class);
78-
$this->expectExceptionMessage('The "places" option must be an array in workflow configuration.');
78+
$this->expectExceptionMessage('The "places" option must be an array or a "FQCN::glob" pattern in workflow configuration.');
7979
$this->createContainerFromClosure(function ($container) {
8080
$container->loadFromExtension('framework', [
8181
'workflows' => [

0 commit comments

Comments
 (0)