-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Workflow] Introduce concept of SupportStrategyInterface #20751
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
Conversation
…r support checks than class instance
@@ -38,6 +38,9 @@ public function process(ContainerBuilder $container) | |||
if (!array_key_exists('marking_store', $tag)) { | |||
throw new RuntimeException(sprintf('The "marking_store" for the tag "workflow.definition" of service "%s" must be set.', $id)); | |||
} | |||
if (!array_key_exists('supports', $tag) && !array_key_exists('support_strategy', $tag)) { |
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.
Before this, "supports" was marked as required in Configuration.php, see below.
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.
This kind of validation should be done in the Configuration
class
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.
K, will fix that.
Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName) | ||
); | ||
$strategyDefinition->setPublic(false); | ||
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClassName)); |
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.
So instead of using the supported class name directly, I am more or less wrapping it in a support strategy service definition.
} | ||
} | ||
if (isset($workflow['support_strategy'])) { | ||
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy']))); |
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.
As a second option, "support_strategy" expects to be a reference to a registered service implementing the introduced SupportStrategyInterface.
@@ -165,6 +165,10 @@ public function testWorkflows() | |||
$this->assertSame(array('workflow.definition' => array(array('name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state'))), $stateMachineDefinition->getTags()); | |||
$this->assertCount(9, $stateMachineDefinition->getArgument(1)); | |||
$this->assertSame('start', $stateMachineDefinition->getArgument(2)); | |||
|
|||
$this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service')); |
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.
Until now, there was not coverage for registry at all. I did not want to introduce too much, but the following should we ok?
@@ -37,16 +37,25 @@ public function __construct($subject, Marking $marking, Transition $transition) | |||
$this->transition = $transition; | |||
} | |||
|
|||
/** | |||
* @return Marking |
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.
Added some docblocks, never bad to have them :)
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 does not bring anything IMHO. and it's not related to your PR.
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.
So should I revert it? Or create a separate PR? For me, adding some of these pointed out some details. Sure I might see that looking at the code, but...
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.
Yes, should be revert. IDEs should be able to infer the return type from code (as the $marking
property is private and is set during initialisation).
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.
Yes, should be reverted. IDEs should be able to infer the return type from code (as the $marking
property is private and is set during initialisation).
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.
K.
*/ | ||
public function add(Workflow $workflow, $className) | ||
public function add(Workflow $workflow, SupportStrategyInterface $supportStrategy) |
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.
So here is the mentioned BC break... Alternatively we could intrdoduce a second method or keep $className and introduce an additional optional parameter for support strategy? Would be ok with my use case and probably with most others as well?
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.
Regardless of architecture (not familiar with the workings), you don't implement an interface here. This means you can safely remove the typehint and do a type check:
@param SupportStrategyInterface|string
public function add(Workflow $workflow, $supportStrategy)
if not instance of SupportStrategyInterface
if not string
throw invalid argument exception
trigger deprecation, argument must be a SupportStrategyInterface as of 4.0
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.
True, one could do that to avoid BC, but it's introducing kind of magic below, isn't it?
I ike the deprecation idea though 👍
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 the standard way of introducing a BC layer in symfony. It's kinda like method overloading
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.
So should I change my code accordingly? ping @lyrixx
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.
@andesk yes. Your current proposal is a BC break, and cannot be accepted for Symfony 3.3 (and would not even be acceptable for Symfony 4.0 as is, as it does not allow to have a progressive upgrade path)
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.
K, will do :)
Should I revert changes in Extension back to inject class name string only instead of wrapping it into the ClassInstanceSupportStrategy? Or keep them as that might be the target for 4.0 anyways?
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.
no, don't revert this one, as injecting class names directly will trigger a deprecation warning. We don't want the normal configuration of the bundle to trigger a deprecation warning for our users (as they would not be in control of fixing it anyway)
@@ -64,6 +65,17 @@ public function testGetWithNoMatch() | |||
$this->assertInstanceOf(Workflow::class, $w1); | |||
$this->assertSame('workflow1', $w1->getName()); | |||
} | |||
|
|||
private function getSupportStrategy($supportedClassName) |
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.
The following might not be the best idea, but keeps the changes minimal...
Hello @andesk Why can't you use the name of workflow to be able to get it? (the second argument of |
Hi @lyrixx Well, we don't want to hardcode the workflow's name to fetch it at all. It's kind of a generic approach to allow having different workflows for different content types in CMS (articles might need extended workflow, images a shorter one, etc), but rely on workflow configuration and the registry to identify the correct one. Is that use case so unexpected? :) |
{ | ||
$this->workflows[] = array($workflow, $className); | ||
if (!$supportStrategy instanceof SupportStrategyInterface) { |
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.
As mentioned in previous comments, to avoid BC break we are allowing string as a fallback, checking it and otherwise throw deprecation warning.
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.
No BC break anymore after doing suggested changes thanks to @iltar Though it seems triggering the deprecated error leads to CI integrations to fail... Any ideas how to fix this?
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.
Thanks to @xabbuh I figured out the @group legacy
annotation. Should be fine now :)
Updated the PR introducing suggested handling to avoid BC break. Anything else? |
Hmmm, fabbot.io is pending since days now. Any ideas how to find out what went wrong there? @nicolas-grekas maybe? |
@@ -324,6 +326,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) | |||
->end() | |||
->end() | |||
->end() | |||
->validate() | |||
->ifTrue(function ($v) { return isset($v['supports']) && isset($v['support_strategy']); }) | |||
->thenInvalid('"supports" and "support_strategy" cannot be used together.') |
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.
Don't we also need to make sure that one of supports
or support_strategy
is configured?
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.
Have not seen other examples for that yet, but of course I can add a second validation for both being !isset()?
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.
something like empty('supports') && empty('support_strategy')
$this->workflows[] = array($workflow, $className); | ||
if (!$supportStrategy instanceof SupportStrategyInterface) { | ||
if (!is_string($supportStrategy)) { | ||
throw new InvalidArgumentException('Expecting instance of SupportStrategyInterface or class name as a string.'); |
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.
I don't think we need to have this check. The docblock is pretty about what types you are allowed to pass.
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.
That was suggested by @iltar and I think it's fine to keep it as a check?
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.
If I am not mistaken, we don't do similar checks anywhere else, but rely on the fact that we claimed through the docblock which types we expect.
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.
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.
Indeed this should be removed.
@@ -23,19 +24,34 @@ class Registry | |||
|
|||
/** | |||
* @param Workflow $workflow |
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.
this parameter needs to be aligned with the following line
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.
Sorry, don't get your point. What do you mean?
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.
Ah, Symfony uses formatting of docblocks with aligned intendions?
* @param null|string $workflowName | ||
* | ||
* @return bool | ||
*/ |
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.
no need for this docblock IMO as the method is private
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.
Several other private methods (e.g. in Workflow.php) have a docblock as well. Is there any rule of thumb when to add it? I personally like having docblocks, but if they are not common in Symfony core I am fine with removing those.
interface SupportStrategyInterface | ||
{ | ||
/** | ||
* @param \Symfony\Component\Workflow\Workflow $workflow |
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.
no need for the FQCN
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.
👍
# Conflicts: # src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
How would you use it from a twig template? |
@lyrixx Not sure what you mean. This change does not break anything in twig usage. And as there was no support to call registry from twig before, I did not have anything to adjust. My current use case: I use the registry service in a REST endpoint. Introducing a twig function is out of scope to me, no? |
If you update the Registry, you have to update this file. And, I don't know how to use these twig functions after that. |
@lyrixx This should work in the same way as before? No changes made to this behavior. Or maybe I don't get your point? The public "API" of registry class stays exactly as it is. The only change is how to decide, which workflow is supposed to be used for a certain object. Until now, only instanceof class was supported, now with introduction of the strategy, anything can be used internally. From the outside, nothing changes. Only change is the (for now) optional configuration of a Strategy service in workflow configuration. |
You are right. I missed something. I'm OK with this PR. But Could you rebase before the merge. Thanks. |
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.
I like this. I just had a minor comment.
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass)); | ||
if (isset($workflow['supports'])) { | ||
foreach ($workflow['supports'] as $supportedClassName) { | ||
$strategyDefinition = new Definition( |
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.
You never register the $strategyDefinition
in the container.
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.
Should be put on one line.
Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName) | ||
); | ||
$strategyDefinition->setPublic(false); | ||
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClassName)); |
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.
You should use $strategyDefinition
here or deprecation notice will always trigger.
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.
Oh wow, how could I not realize that?! Thank you @Nyholm , will fix
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.
Done.
# Conflicts: # src/Symfony/Component/Workflow/Registry.php
* | ||
* @return Workflow | ||
* @throws InvalidArgumentException |
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.
missing empty line before this line
{ | ||
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock(); | ||
$strategy->expects($this->any())->method('supports') | ||
->will($this->returnCallback(function($workflow, $subject) use ($supportedClassName) { |
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.
missing space after function
@@ -436,7 +436,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde | |||
|
|||
// Create MarkingStore | |||
if (isset($workflow['marking_store']['type'])) { | |||
$markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.'.$workflow['marking_store']['type']); | |||
$markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.' . $workflow['marking_store']['type']); |
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.
to be reverted
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition)); | ||
} | ||
} | ||
if (isset($workflow['support_strategy'])) { |
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.
could probably be an elseif
as the two options are exclusive
if (isset($workflow['supports'])) { | ||
foreach ($workflow['supports'] as $supportedClassName) { | ||
$strategyDefinition = new Definition( | ||
Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName) |
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.
You should use a use
statement instead of using the FQCN here.
$this->workflows[] = array($workflow, $className); | ||
if (!$supportStrategy instanceof SupportStrategyInterface) { | ||
if (!is_string($supportStrategy)) { | ||
throw new InvalidArgumentException('Expecting instance of SupportStrategyInterface or class name as a string.'); |
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.
Indeed this should be removed.
} | ||
|
||
/** | ||
* @param object $subject | ||
* @param string|null $workflowName | ||
* @param null|string $workflowName |
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.
should be reverted
|
||
class ClassInstanceSupportStrategy implements SupportStrategyInterface | ||
{ | ||
/** @var string */ |
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.
Comment should be removed
|
||
/** | ||
* @param string $className | ||
*/ |
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.
docblock should be removed
@fabpot Fixed proposed changes, fabbot.io still complaining about code style in FrameworkExtension class not related to this story. Anything I can do about it? |
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass)); | ||
if (isset($workflow['supports'])) { | ||
foreach ($workflow['supports'] as $supportedClassName) { | ||
$strategyDefinition = new Definition( |
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.
Should be put on one line.
$this->assertSame('workflow2', $workflow->getName()); | ||
} | ||
|
||
private function getSupportStrategy($supportedClassName) |
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.
Should be named createSupportStrategy
|
||
private function getSupportStrategy($supportedClassName) | ||
{ | ||
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock(); |
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.
$this->getMock(SupportStrategyInterface::class)
is enough
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.
According to #20984 the current syntax should be kept
{ | ||
if (!$subject instanceof $className) { | ||
if (is_string($supportStrategy) && !$subject instanceof $supportStrategy) { |
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.
Could you add a comment before this line:
// Keep BC. To be removed in 4.0
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.
Rather than dealing with BC here, I would convert the string to a SupportStrategy when setting it 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.
So @stof , you mean that inside the add() method I would create a ClassInstanceSupportStrategy in the deprecation case?
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.
Should I still add a BC comment somewhere? ping @lyrixx ?
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.
The two conditions could then be simplified to:
if (!$supportStrategy->supports($workflow, $subject)) {
return false;
}
return true; | ||
} | ||
|
||
return $name === $workflow->getName(); | ||
return $workflowName === $workflow->getName(); |
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.
All this method could be simplified:
if (null !== $workflowName && $workflowName !== $workflow->getName()) {
return false;
}
// Keep BC. To be removed in 4.0
if (is_string($supportStrategy)) {
return $subject instanceof $supportStrategy;
}
return $supportStrategy->supports($workflow, $subject);
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.
So if @stof 's idea above will be implemented, this can even be more simplified, right?
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.
indeed.
(Please, ping me when you're ready. Thanks)
{ | ||
$registry = new Registry(); | ||
|
||
$registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class); |
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.
$this->getMock(SupportStrategyInterface::class)
is enough
(same bellow)
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.
According to #20984 the current syntax should be kept
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock(); | ||
$strategy->expects($this->any())->method('supports') | ||
->will($this->returnCallback(function ($workflow, $subject) use ($supportedClassName) { | ||
return $subject instanceof $supportedClassName; |
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.
Why do you use a mock here when you can use the ClassInstanceSupportStrategy
?
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.
Don't know how this is handled in Symfony environment, but as I want to test the registry alone I personally prefer mocking all other collaborators. Should I change it?
Hello @andesk Could you take time to finish this PR? If not, I could finish it for you if you allow me to push in your fork. Thanks ;) |
@@ -287,6 +286,9 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) | |||
->end() | |||
->end() | |||
->end() | |||
->scalarNode('support_strategy') | |||
->cannotBeEmpty() |
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.
this forbids resetting it to null in a later file to use supports
again
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.
So should I simply remove it?
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.
ping @stof
@@ -325,6 +327,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) | |||
->end() | |||
->end() | |||
->end() | |||
->validate() | |||
->ifTrue(function ($v) { return isset($v['supports']) && isset($v['support_strategy']); }) |
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.
supports
will always be set as it is a prototyped array node and so has a default. You need to check whether it is the empty array 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.
So return !empty($v['supports']) && isset($v['support_strategy']);
?
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.
yes
@@ -458,8 +459,16 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde | |||
$container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition); | |||
|
|||
// Add workflow to Registry | |||
foreach ($workflow['supports'] as $supportedClass) { | |||
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass)); | |||
if (isset($workflow['supports'])) { |
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 is always set here. You should check for the array not being empty
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.
So if (!empty($workflow['supports']))
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.
yes
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.
having a test covering this combination would probably be good
} | ||
|
||
/** | ||
* @param object $subject | ||
* @param string|null $workflowName | ||
* | ||
* @return Workflow | ||
* | ||
* @throws InvalidArgumentException |
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.
we don't add this phpdoc when we cannot document in which case the exception is thrown
{ | ||
if (!$subject instanceof $className) { | ||
if (is_string($supportStrategy) && !$subject instanceof $supportStrategy) { |
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.
Rather than dealing with BC here, I would convert the string to a SupportStrategy when setting it instead.
|
||
use Symfony\Component\Workflow\Workflow; | ||
|
||
class ClassInstanceSupportStrategy implements SupportStrategyInterface |
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.
you should add a docblock and add yourself as an author
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.
same for the interface
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.
and the class should become final
{ | ||
private $className; | ||
|
||
public function __construct($className) |
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.
you should add a docblock documenting the argument as a string
Closing in favor of #21334 |
…ce (andesk, lyrixx) This PR was merged into the 3.3-dev branch. Discussion ---------- [Workflow] Introduce concept of SupportStrategyInterface | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #20751 | License | MIT | Doc PR | - Commits ------- 134a58b [Workflow] Fixed code and tests 1843012 [Workflow] Introduce concept of SupprtStrategyInterface to allow other support checks than class instance
This PR was merged into the 3.3-dev branch. Discussion ---------- [Workflow] sync the changelog | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20751, #21334, #21933, #21935, #21950 | License | MIT | Doc PR | Commits ------- 98a18ee [Workflow] sync the changelog
As discussed during #symfonycon with @Nyholm and @xabbuh , in it's first implementation, the Workflow component's Registry class is checking on a subject's class instance to decide, if it is supported or not. In our current project we are using a CMS which uses a generic class to hold content of any content type. But we would want to be able to use the registry to identify the supported workflow for a content by checking the class's content type. As this is a very propriatary use case, but I believe similar requirements might be coming for other projects, I decided to introduce a strategy pattern to generalize it.
Disclaimer: Current state is PoC to indicate the need, feel free to find/suggest a different approach or give me directions to adjust/change it.
As the Registry:add() method expected a $className (string) attribute and I changed it to accept any SupportStrategyInterface instead, this PR introduces a BC break. As the component is quite fresh and the Registry is kind of a helper construct, we might be ok to introduce this BC nevertheless? I tried to keep the "supports" configuration key as it is to avoid conflicts with existing workflow service definitions and keep the changes as local as possible. Other ideas to avoid this are welcome of course :)
While adjusting the existing tests for FrameworkBundle I realized that probably not every Workflow related code is covered by tests yet. I decided to skip adding more tests (at least for now) as we first need to agree on if we introduce a generalized approach at all and if the current one is acceptable to others.