[Workflow] Introduce concept of SupprtStrategyInterface to allow other support checks than class instance

This commit is contained in:
Andreas Kleemann 2016-12-04 22:08:15 +00:00 committed by Grégoire Pineau
parent 5921530a1a
commit 184301206f
8 changed files with 162 additions and 18 deletions

View File

@ -280,7 +280,6 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->arrayNode('supports')
->isRequired()
->beforeNormalization()
->ifString()
->then(function ($v) { return array($v); })
@ -293,6 +292,9 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end()
->scalarNode('support_strategy')
->cannotBeEmpty()
->end()
->scalarNode('initial_place')->defaultNull()->end()
->arrayNode('places')
->isRequired()
@ -353,6 +355,10 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end()
->validate()
->ifTrue(function ($v) { return isset($v['supports']) && isset($v['support_strategy']); })
->thenInvalid('"supports" and "support_strategy" cannot be used together.')
->end()
->end()
->end()
->end()

View File

@ -36,6 +36,7 @@ use Symfony\Component\Serializer\Normalizer\DataUriNormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
use Symfony\Component\Workflow;
use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy;
use Symfony\Component\Yaml\Yaml;
/**
@ -475,8 +476,14 @@ class FrameworkExtension extends Extension
$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'])) {
foreach ($workflow['supports'] as $supportedClassName) {
$strategyDefinition = new Definition(ClassInstanceSupportStrategy::class, array($supportedClassName));
$strategyDefinition->setPublic(false);
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition));
}
} elseif (isset($workflow['support_strategy'])) {
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy'])));
}
}
}

View File

@ -168,11 +168,16 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertCount(9, $stateMachineDefinition->getArgument(1));
$this->assertSame('start', $stateMachineDefinition->getArgument(2));
$serviceMarkingStoreWorkflowDefinition = $container->getDefinition('workflow.service_marking_store_workflow');
/** @var Reference $markingStoreRef */
$markingStoreRef = $serviceMarkingStoreWorkflowDefinition->getArgument(1);
$this->assertInstanceOf(Reference::class, $markingStoreRef);
$this->assertEquals('workflow_service', (string) $markingStoreRef);
$this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service'));
$registryDefinition = $container->getDefinition('workflow.registry');
$this->assertGreaterThan(0, count($registryDefinition->getMethodCalls()));
}
/**

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Workflow;
use Symfony\Component\Workflow\Exception\InvalidArgumentException;
use Symfony\Component\Workflow\SupportStrategy\SupportStrategyInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
@ -22,12 +23,16 @@ class Registry
private $workflows = array();
/**
* @param Workflow $workflow
* @param string $className
* @param Workflow $workflow
* @param string|SupportStrategyInterface $supportStrategy
*/
public function add(Workflow $workflow, $className)
public function add(Workflow $workflow, $supportStrategy)
{
$this->workflows[] = array($workflow, $className);
if (!$supportStrategy instanceof SupportStrategyInterface) {
@trigger_error('Support of class name string was deprecated after version 3.2 and won\'t work anymore in 4.0.', E_USER_DEPRECATED);
}
$this->workflows[] = array($workflow, $supportStrategy);
}
/**
@ -40,8 +45,8 @@ class Registry
{
$matched = null;
foreach ($this->workflows as list($workflow, $className)) {
if ($this->supports($workflow, $className, $subject, $workflowName)) {
foreach ($this->workflows as list($workflow, $supportStrategy)) {
if ($this->supports($workflow, $supportStrategy, $subject, $workflowName)) {
if ($matched) {
throw new InvalidArgumentException('At least two workflows match this subject. Set a different name on each and use the second (name) argument of this method.');
}
@ -56,16 +61,19 @@ class Registry
return $matched;
}
private function supports(Workflow $workflow, $className, $subject, $name)
private function supports(Workflow $workflow, $supportStrategy, $subject, $workflowName)
{
if (!$subject instanceof $className) {
if (is_string($supportStrategy) && !$subject instanceof $supportStrategy) {
return false;
}
if ($supportStrategy instanceof SupportStrategyInterface && !$supportStrategy->supports($workflow, $subject)) {
return false;
}
if (null === $name) {
if (null === $workflowName) {
return true;
}
return $name === $workflow->getName();
return $workflowName === $workflow->getName();
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Symfony\Component\Workflow\SupportStrategy;
use Symfony\Component\Workflow\Workflow;
class ClassInstanceSupportStrategy implements SupportStrategyInterface
{
private $className;
public function __construct($className)
{
$this->className = $className;
}
/**
* {@inheritdoc}
*/
public function supports(Workflow $workflow, $subject)
{
return $subject instanceof $this->className;
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Workflow\SupportStrategy;
use Symfony\Component\Workflow\Workflow;
interface SupportStrategyInterface
{
/**
* @param Workflow $workflow
* @param object $subject
*
* @return bool
*/
public function supports(Workflow $workflow, $subject);
}

View File

@ -6,6 +6,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\SupportStrategy\SupportStrategyInterface;
use Symfony\Component\Workflow\Workflow;
class RegistryTest extends \PHPUnit_Framework_TestCase
@ -14,13 +15,11 @@ class RegistryTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$workflows = array();
$this->registry = new Registry();
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class);
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), Subject2::class);
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow3'), Subject2::class);
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), $this->createSupportStrategy(Subject1::class));
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), $this->createSupportStrategy(Subject2::class));
$this->registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow3'), $this->createSupportStrategy(Subject2::class));
}
protected function tearDown()
@ -64,6 +63,40 @@ class RegistryTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf(Workflow::class, $w1);
$this->assertSame('workflow1', $w1->getName());
}
/**
* @group legacy
*/
public function testGetWithSuccessLegacyStrategy()
{
$registry = new Registry();
$registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow1'), Subject1::class);
$registry->add(new Workflow(new Definition(array(), array()), $this->getMockBuilder(MarkingStoreInterface::class)->getMock(), $this->getMockBuilder(EventDispatcherInterface::class)->getMock(), 'workflow2'), Subject2::class);
$workflow = $registry->get(new Subject1());
$this->assertInstanceOf(Workflow::class, $workflow);
$this->assertSame('workflow1', $workflow->getName());
$workflow = $registry->get(new Subject1(), 'workflow1');
$this->assertInstanceOf(Workflow::class, $workflow);
$this->assertSame('workflow1', $workflow->getName());
$workflow = $registry->get(new Subject2(), 'workflow2');
$this->assertInstanceOf(Workflow::class, $workflow);
$this->assertSame('workflow2', $workflow->getName());
}
private function createSupportStrategy($supportedClassName)
{
$strategy = $this->getMockBuilder(SupportStrategyInterface::class)->getMock();
$strategy->expects($this->any())->method('supports')
->will($this->returnCallback(function ($workflow, $subject) use ($supportedClassName) {
return $subject instanceof $supportedClassName;
}));
return $strategy;
}
}
class Subject1

View File

@ -0,0 +1,37 @@
<?php
namespace Symfony\Component\Workflow\Tests\SupportStrategy;
use Symfony\Component\Workflow\SupportStrategy\ClassInstanceSupportStrategy;
use Symfony\Component\Workflow\Workflow;
class ClassInstanceSupportStrategyTest extends \PHPUnit_Framework_TestCase
{
public function testSupportsIfClassInstance()
{
$strategy = new ClassInstanceSupportStrategy('Symfony\Component\Workflow\Tests\SupportStrategy\Subject1');
$this->assertTrue($strategy->supports($this->getWorkflow(), new Subject1()));
}
public function testSupportsIfNotClassInstance()
{
$strategy = new ClassInstanceSupportStrategy('Symfony\Component\Workflow\Tests\SupportStrategy\Subject2');
$this->assertFalse($strategy->supports($this->getWorkflow(), new Subject1()));
}
private function getWorkflow()
{
return $this->getMockBuilder(Workflow::class)
->disableOriginalConstructor()
->getMock();
}
}
class Subject1
{
}
class Subject2
{
}