Commit af788c8d authored by bojanz's avatar bojanz

Implement guards.

parent 7ae76b6a
......@@ -57,6 +57,16 @@ default:
to: canceled
```
Transitions can be further restricted by [guards](https://github.com/bojanz/commerce_workflow/blob/8.x-1.x/src/WorkflowGuard/WorkflowGuardInterface.php), which are implemented as tagged services:
```yaml
mymodule.fulfillment_guard:
class: Drupal\mymodule\WorkflowGuard\FulfillmentGuard
tags:
- { name: commerce_workflow.workflow_guard, group: order }
```
The group argument allows the guard factory to only instantiate the guards relevant
to a specific workflow group.
The current state is stored in a [StateItem](https://github.com/bojanz/commerce_workflow/blob/8.x-1.x/src/Plugin/Field/FieldType/StateItem.php) field.
A field setting specifies the used workflow, or a value callback that allows
the workflow to be resolved at runtime (checkout workflow based on the used plugin, etc.
......
services:
commerce_workflow.workflow_guard_factory:
class: Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactory
arguments: ['@service_container']
plugin.manager.workflow:
class: Drupal\commerce_workflow\WorkflowManager
arguments: ['@module_handler', '@cache.discovery', '@plugin.manager.workflow_group']
......
<?php
/**
* @file
* Contains \Drupal\commerce_workflow\CommerceWorkflowServiceProvider.
*/
namespace Drupal\commerce_workflow;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\commerce_workflow\DependencyInjection\Compiler\WorkflowGuardsPass;
/**
* Registers the workflow guard compiler pass.
*/
class CommerceWorkflowServiceProvider implements ServiceProviderInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
$container->addCompilerPass(new WorkflowGuardsPass());
}
}
<?php
/**
* @file
* Contains \Drupal\commerce_workflow\DependencyInjection\Compiler\WorkflowGuardsPass.
*/
namespace Drupal\commerce_workflow\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds the context provider service IDs to the context manager.
*/
class WorkflowGuardsPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*
* Passes the grouped service IDs of workflow guards to the guard factory.
*/
public function process(ContainerBuilder $container) {
$guards = [];
foreach ($container->findTaggedServiceIds('commerce_workflow.workflow_guard') as $id => $attributes) {
if (empty($attributes[0]['group'])) {
continue;
}
$guards[$attributes[0]['group']][] = $id;
}
$definition = $container->getDefinition('commerce_workflow.workflow_guard_factory');
$definition->addArgument($guards);
}
}
......@@ -7,13 +7,23 @@
namespace Drupal\commerce_workflow\Plugin\Workflow;
use Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the class for workflows.
*/
class Workflow extends PluginBase implements WorkflowInterface {
class Workflow extends PluginBase implements WorkflowInterface, ContainerFactoryPluginInterface {
/**
* The workflow guard factory.
*
* @var \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactoryInterface
*/
protected $guardFactory;
/**
* The initialized states.
......@@ -30,11 +40,21 @@ class Workflow extends PluginBase implements WorkflowInterface {
protected $transitions = [];
/**
* {@inheritdoc}
* Constructs a new Workflow object.
*
* @param array $configuration
* The plugin configuration.
* @param string $plugin_id
* The workflow plugin_id.
* @param mixed $plugin_definition
* The workflow plugin implementation definition.
* @param \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactoryInterface $guard_factory
* The workflow guard factory.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, WorkflowGuardFactoryInterface $guard_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->guardFactory = $guard_factory;
// Populate value objects for states and transitions.
foreach ($plugin_definition['states'] as $id => $state_definition) {
$this->states[$id] = new WorkflowState($id, $state_definition['label']);
......@@ -50,6 +70,18 @@ class Workflow extends PluginBase implements WorkflowInterface {
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('commerce_workflow.workflow_guard_factory')
);
}
/**
* {@inheritdoc}
*/
......@@ -135,7 +167,12 @@ class Workflow extends PluginBase implements WorkflowInterface {
* TRUE if the transition is allowed, FALSE otherwise.
*/
protected function isTransitionAllowed(WorkflowTransition $transition, EntityInterface $entity) {
// @todo
foreach ($this->guardFactory->get($this->getGroup()) as $guard) {
if ($guard->allowed($transition, $this, $entity) === FALSE) {
return FALSE;
}
}
return TRUE;
}
......
<?php
/**
* @file
* Contains \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactory.
*/
namespace Drupal\commerce_workflow\WorkflowGuard;
use Symfony\Component\DependencyInjection\ContainerInterface;
class WorkflowGuardFactory {
/**
* The service container.
*
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* The guard service ids, grouped by workflow group id.
*
* @var string[]
*/
protected $guardServiceIds;
/**
* Constructs a new WorkflowGuardFactory object.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The service container.
* @param string[] $guard_service_ids
* The guard service ids, grouped by workflow group id
*/
public function __construct(ContainerInterface $container, array $guard_service_ids) {
$this->container = $container;
$this->guardServiceIds = $guard_service_ids;
}
/**
* {@inheritdoc}
*/
public function get($group_id) {
if (!isset($this->guardServiceIds[$group_id])) {
return [];
}
$guards = [];
foreach ($this->guardServiceIds[$group_id] as $service_id) {
$guards[] = $this->container->get($service_id);
}
return $guards;
}
}
<?php
/**
* @file
* Contains \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardFactoryInterface.
*/
namespace Drupal\commerce_workflow\WorkflowGuard;
/**
* Defines the interface for workflow guard factories.
*/
interface WorkflowGuardFactoryInterface {
/**
* Gets the instantiated workflow guards for the given group id.
*
* @param string $group_id
* The group id.
*
* @return \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardInterface[]
*/
public function get($group_id);
}
<?php
/**
* @file
* Contains \Drupal\commerce_workflow\WorkflowGuard\WorkflowGuardInterface.
*/
namespace Drupal\commerce_workflow\WorkflowGuard;
use Drupal\commerce_workflow\Plugin\Workflow\WorkflowInterface;
use Drupal\commerce_workflow\Plugin\Workflow\WorkflowTransition;
use Drupal\Core\Entity\EntityInterface;
/**
* Defines the interface for workflow guards.
*
* Allows for custom logic controling the availability of specific transitions.
* Transitions could be restricted based on the current user's permissions, a
* parent entity field, etc.
*
* By default, a transition is allowed unless at least one guard returns FALSE.
*/
interface WorkflowGuardInterface {
/**
* Checks whether the given transition is allowed.
*
* @param \Drupal\commerce_workflow\Plugin\Workflow\WorkflowTransition $transition
* The transition.
* @param \Drupal\commerce_workflow\Plugin\Workflow\WorkflowInterface $workflow
* The workflow.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The parent entity.
*
* @return bool
* TRUE if the transition is allowed, FALSE otherwise.
*/
public function allowed(WorkflowTransition $transition, WorkflowInterface $workflow, EntityInterface $entity);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment