Skip to content

Commit 438cbf0

Browse files
committed
feature #54344 [Workflow] Add EventNameTrait to compute event name strings in subscribers (squrious)
This PR was squashed before being merged into the 7.1 branch. Discussion ---------- [Workflow] Add EventNameTrait to compute event name strings in subscribers | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | N/A | License | MIT Hello! Using the event dispatcher, we usually use event's class name to configure the event to listen to. For workflow, we still have to use raw strings: ```php class WorkflowPostSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ 'workflow.post.entered.published' => 'onPublishedEntered', ]; } } ``` Using class names is more clear about what event we use (easier to know which event to use in the listener). Even if we already have attributes to define event listeners, the event subscriber way could be improved. ### Proposal This PR adds a trait to improve DX when using workflow events in event subscribers. ```php class WorkflowPostSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ PublishedEvent::get(workflowName: 'post', placeName: 'entered') => 'onPublishedEntered', ]; } } ``` For a better DX, the `EventNameTrait` provides two methods: `getNameForPlace` and `getNameForTransition`, so the second argument of `::get` and its PHPDoc are consistent with the event type. In event classes, it is used like: ```php class EnterEvent extends Event { use EventNameTrait { use getNameForPlace as public get; } // ... } ``` Cheers! Commits ------- 742221f14e [Workflow] Add EventNameTrait to compute event name strings in subscribers
2 parents 8134ea9 + 3a084f1 commit 438cbf0

10 files changed

+157
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add method `getEnabledTransition()` to `WorkflowInterface`
88
* Automatically register places from transitions
99
* Add support for workflows that need to store many tokens in the marking
10+
* Add method `getName()` in event classes to build event names in subscribers
1011

1112
7.0
1213
---

Event/AnnounceEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class AnnounceEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForTransition as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Event/CompletedEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class CompletedEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForTransition as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Event/EnterEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class EnterEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForPlace as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Event/EnteredEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class EnteredEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForPlace as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Event/EventNameTrait.php

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Workflow\Event;
13+
14+
use Symfony\Component\Workflow\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Nicolas Rigaud <squrious@protonmail.com>
18+
*
19+
* @internal
20+
*/
21+
trait EventNameTrait
22+
{
23+
/**
24+
* Gets the event name for workflow and transition.
25+
*
26+
* @throws InvalidArgumentException If $transitionName is provided without $workflowName
27+
*/
28+
private static function getNameForTransition(?string $workflowName, ?string $transitionName): string
29+
{
30+
return self::computeName($workflowName, $transitionName);
31+
}
32+
33+
/**
34+
* Gets the event name for workflow and place.
35+
*
36+
* @throws InvalidArgumentException If $placeName is provided without $workflowName
37+
*/
38+
private static function getNameForPlace(?string $workflowName, ?string $placeName): string
39+
{
40+
return self::computeName($workflowName, $placeName);
41+
}
42+
43+
private static function computeName(?string $workflowName, ?string $transitionOrPlaceName): string
44+
{
45+
$eventName = strtolower(basename(str_replace('\\', '/', static::class), 'Event'));
46+
47+
if (null === $workflowName) {
48+
if (null !== $transitionOrPlaceName) {
49+
throw new \InvalidArgumentException('Missing workflow name.');
50+
}
51+
52+
return sprintf('workflow.%s', $eventName);
53+
}
54+
55+
if (null === $transitionOrPlaceName) {
56+
return sprintf('workflow.%s.%s', $workflowName, $eventName);
57+
}
58+
59+
return sprintf('workflow.%s.%s.%s', $workflowName, $eventName, $transitionOrPlaceName);
60+
}
61+
}

Event/GuardEvent.php

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
*/
2424
final class GuardEvent extends Event
2525
{
26+
use EventNameTrait {
27+
getNameForTransition as public getName;
28+
}
29+
2630
private TransitionBlockerList $transitionBlockerList;
2731

2832
public function __construct(object $subject, Marking $marking, Transition $transition, ?WorkflowInterface $workflow = null)

Event/LeaveEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class LeaveEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForPlace as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Event/TransitionEvent.php

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
final class TransitionEvent extends Event
1919
{
20+
use EventNameTrait {
21+
getNameForTransition as public getName;
22+
}
2023
use HasContextTrait;
2124

2225
public function __construct(object $subject, Marking $marking, ?Transition $transition = null, ?WorkflowInterface $workflow = null, array $context = [])

Tests/Event/EventNameTraitTest.php

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Workflow\Tests\Event;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Workflow\Event\AnnounceEvent;
16+
use Symfony\Component\Workflow\Event\CompletedEvent;
17+
use Symfony\Component\Workflow\Event\EnteredEvent;
18+
use Symfony\Component\Workflow\Event\EnterEvent;
19+
use Symfony\Component\Workflow\Event\GuardEvent;
20+
use Symfony\Component\Workflow\Event\LeaveEvent;
21+
use Symfony\Component\Workflow\Event\TransitionEvent;
22+
23+
class EventNameTraitTest extends TestCase
24+
{
25+
/**
26+
* @dataProvider getEvents
27+
*
28+
* @param class-string $class
29+
*/
30+
public function testEventNames(string $class, ?string $workflowName, ?string $transitionOrPlaceName, string $expected)
31+
{
32+
$name = $class::getName($workflowName, $transitionOrPlaceName);
33+
$this->assertEquals($expected, $name);
34+
}
35+
36+
public static function getEvents(): iterable
37+
{
38+
yield [AnnounceEvent::class, null, null, 'workflow.announce'];
39+
yield [AnnounceEvent::class, 'post', null, 'workflow.post.announce'];
40+
yield [AnnounceEvent::class, 'post', 'publish', 'workflow.post.announce.publish'];
41+
42+
yield [CompletedEvent::class, null, null, 'workflow.completed'];
43+
yield [CompletedEvent::class, 'post', null, 'workflow.post.completed'];
44+
yield [CompletedEvent::class, 'post', 'publish', 'workflow.post.completed.publish'];
45+
46+
yield [EnteredEvent::class, null, null, 'workflow.entered'];
47+
yield [EnteredEvent::class, 'post', null, 'workflow.post.entered'];
48+
yield [EnteredEvent::class, 'post', 'published', 'workflow.post.entered.published'];
49+
50+
yield [EnterEvent::class, null, null, 'workflow.enter'];
51+
yield [EnterEvent::class, 'post', null, 'workflow.post.enter'];
52+
yield [EnterEvent::class, 'post', 'published', 'workflow.post.enter.published'];
53+
54+
yield [GuardEvent::class, null, null, 'workflow.guard'];
55+
yield [GuardEvent::class, 'post', null, 'workflow.post.guard'];
56+
yield [GuardEvent::class, 'post', 'publish', 'workflow.post.guard.publish'];
57+
58+
yield [LeaveEvent::class, null, null, 'workflow.leave'];
59+
yield [LeaveEvent::class, 'post', null, 'workflow.post.leave'];
60+
yield [LeaveEvent::class, 'post', 'published', 'workflow.post.leave.published'];
61+
62+
yield [TransitionEvent::class, null, null, 'workflow.transition'];
63+
yield [TransitionEvent::class, 'post', null, 'workflow.post.transition'];
64+
yield [TransitionEvent::class, 'post', 'publish', 'workflow.post.transition.publish'];
65+
}
66+
67+
public function testInvalidArgumentExceptionIsThrownIfWorkflowNameIsMissing()
68+
{
69+
$this->expectException(\InvalidArgumentException::class);
70+
71+
EnterEvent::getName(null, 'place');
72+
}
73+
}

0 commit comments

Comments
 (0)