Commit 1b5b2fc0 authored by bojanz's avatar bojanz

Issue #2897294 by bojanz: Allow filtering conditions for a specific parent entity type

parent cf8fc176
......@@ -58,7 +58,7 @@ services:
plugin.manager.commerce_condition:
class: Drupal\commerce\ConditionManager
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@entity_type.manager']
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@entity_type.manager', '@event_dispatcher']
plugin.manager.commerce_entity_trait:
class: Drupal\commerce\EntityTraitManager
......
......@@ -112,6 +112,7 @@ class PaymentGatewayForm extends CommercePluginEntityFormBase {
$form['conditions'] = [
'#type' => 'commerce_conditions',
'#title' => $this->t('Conditions'),
'#parent_entity_type' => 'commerce_payment_gateway',
'#entity_types' => ['commerce_order'],
'#default_value' => $gateway->get('conditions'),
];
......
......@@ -15,6 +15,11 @@ services:
tags:
- { name: backend_overridable }
commerce_promotion.filter_conditions_subscriber:
class: Drupal\commerce_promotion\EventSubscriber\FilterConditionsEventSubscriber
tags:
- { name: event_subscriber }
commerce_promotion.order_subscriber:
class: Drupal\commerce_promotion\EventSubscriber\OrderEventSubscriber
arguments: ['@entity_type.manager', '@commerce_promotion.usage']
......
<?php
namespace Drupal\commerce_promotion\EventSubscriber;
use Drupal\commerce\Event\FilterConditionsEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class FilterConditionsEventSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [
'commerce.filter_conditions' => 'onFilterConditions',
];
return $events;
}
/**
* Removes unneeded conditions.
*
* Promotions have store and order_types base fields that are used for
* filtering, so there's no need to have conditions targeting the same data.
*
* @param \Drupal\commerce\Event\FilterConditionsEvent $event
* The event.
*/
public function onFilterConditions(FilterConditionsEvent $event) {
if ($event->getParentEntityTypeId() == 'commerce_promotion') {
$definitions = $event->getDefinitions();
unset($definitions['order_store']);
$event->setDefinitions($definitions);
}
}
}
......@@ -52,8 +52,8 @@ class CommerceCondition extends Plugin {
/**
* The condition entity type ID.
*
* This is the entity type ID of the entity passed to the plugin during evaluation.
* For example: 'commerce_order'.
* This is the entity type ID of the entity passed to the plugin during
* evaluation. For example: 'commerce_order'.
*
* @var string
*/
......
......@@ -2,12 +2,15 @@
namespace Drupal\commerce;
use Drupal\commerce\Event\CommerceEvents;
use Drupal\commerce\Event\FilterConditionsEvent;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Manages discovery and instantiation of condition plugins.
......@@ -26,6 +29,13 @@ class ConditionManager extends DefaultPluginManager implements ConditionManagerI
*/
protected $entityTypeManager;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Constructs a new ConditionManager object.
*
......@@ -38,13 +48,16 @@ class ConditionManager extends DefaultPluginManager implements ConditionManagerI
* The module handler.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager) {
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher) {
parent::__construct('Plugin/Commerce/Condition', $namespaces, $module_handler, 'Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface', 'Drupal\commerce\Annotation\CommerceCondition');
$this->alterInfo('commerce_condition_info');
$this->setCacheBackend($cache_backend, 'commerce_condition_plugins');
$this->entityTypeManager = $entity_type_manager;
$this->eventDispatcher = $event_dispatcher;
}
/**
......@@ -68,14 +81,14 @@ class ConditionManager extends DefaultPluginManager implements ConditionManagerI
/**
* {@inheritdoc}
*/
public function getDefinitionsByEntityTypes(array $entity_types) {
$definitions = $this->getDefinitions();
if (!empty($entity_types)) {
// Remove conditions not matching the specified entity types.
$definitions = array_filter($definitions, function ($definition) use ($entity_types) {
return in_array($definition['entity_type'], $entity_types);
});
}
public function getFilteredDefinitions($parent_entity_type_id, array $entity_type_ids) {
$definitions = array_filter($this->getDefinitions(), function ($definition) use ($entity_type_ids) {
return in_array($definition['entity_type'], $entity_type_ids);
});
// Certain conditions might be unavailable to the given parent entity type.
$event = new FilterConditionsEvent($definitions, $parent_entity_type_id);
$this->eventDispatcher->dispatch(CommerceEvents::FILTER_CONDITIONS, $event);
$definitions = $event->getDefinitions();
return $definitions;
}
......
......@@ -10,14 +10,18 @@ use Drupal\Component\Plugin\CategorizingPluginManagerInterface;
interface ConditionManagerInterface extends CategorizingPluginManagerInterface {
/**
* Gets the plugin definitions for the given entity types.
* Gets the filtered plugin definitions.
*
* @param array $entity_types
* The entity type IDs.
* @param string $parent_entity_type_id
* The parent entity type ID. For example: 'commerce_promotion' if the
* conditions are being loaded for a promotion.
* @param array $entity_type_ids
* The entity type IDs. For example: ['commerce_order'] to get
* only conditions that evaluate orders.
*
* @return array
* The plugin definitions.
* The filtered plugin definitions.
*/
public function getDefinitionsByEntityTypes(array $entity_types);
public function getFilteredDefinitions($parent_entity_type_id, array $entity_type_ids);
}
......@@ -16,6 +16,7 @@ use Drupal\Core\Render\Element\FormElement;
* $form['conditions'] = [
* '#type' => 'commerce_conditions',
* '#title' => 'Conditions',
* '#parent_entity_type' => 'commerce_promotion',
* '#entity_types' => ['commerce_order', 'commerce_order_item'],
* '#default_value' => [
* [
......@@ -46,6 +47,7 @@ class Conditions extends FormElement {
return [
'#input' => TRUE,
'#tree' => TRUE,
'#parent_entity_type' => NULL,
'#entity_types' => [],
'#default_value' => [],
'#title' => '',
......@@ -79,17 +81,27 @@ class Conditions extends FormElement {
* The processed element.
*
* @throws \InvalidArgumentException
* Thrown for malformed #default_value properties.
* Thrown for missing or malformed #parent_entity_type, #entity_types,
* #default_value properties.
*/
public static function processConditions(array &$element, FormStateInterface $form_state, array &$complete_form) {
if (empty($element['#parent_entity_type'])) {
throw new \InvalidArgumentException('The commerce_conditions element requires the #parent_entity_type property.');
}
if (empty($element['#entity_types'])) {
throw new \InvalidArgumentException('The commerce_conditions element requires the #entity_types property.');
}
if (!is_array($element['#entity_types'])) {
throw new \InvalidArgumentException('The commerce_conditions #entity_types property must be an array.');
}
if (!is_array($element['#default_value'])) {
throw new \InvalidArgumentException('The commerce_conditions #default_value must be an array.');
throw new \InvalidArgumentException('The commerce_conditions #default_value property must be an array.');
}
$default_value = array_column($element['#default_value'], 'configuration', 'plugin');
/** @var \Drupal\commerce\ConditionManagerInterface $plugin_manager */
$plugin_manager = \Drupal::service('plugin.manager.commerce_condition');
$definitions = $plugin_manager->getDefinitionsByEntityTypes($element['#entity_types']);
$definitions = $plugin_manager->getFilteredDefinitions($element['#parent_entity_type'], $element['#entity_types']);
$grouped_definitions = [];
foreach ($definitions as $plugin_id => $definition) {
$category = (string) $definition['category'];
......
......@@ -9,6 +9,15 @@ namespace Drupal\commerce\Event;
*/
final class CommerceEvents {
/**
* Name of the event fired when filtering available conditions.
*
* @Event
*
* @see \Drupal\commerce\Event\FilterConditionsEvent
*/
const FILTER_CONDITIONS = 'commerce.filter_conditions';
/**
* Name of the event fired when altering the referenceable plugin types.
*
......
<?php
namespace Drupal\commerce\Event;
use Symfony\Component\EventDispatcher\Event;
/**
* Defines the event for filtering the available conditions.
*
* @see \Drupal\commerce_payment\Event\PaymentEvents
*/
class FilterConditionsEvent extends Event {
/**
* The condition definitions.
*
* @var array
*/
protected $definitions;
/**
* The parent entity type ID.
*
* @var string
*/
protected $parentEntityTypeId;
/**
* Constructs a new FilterConditionsEvent object.
*
* @param array $definitions
* The condition definitions.
* @param string $parent_entity_type_id
* The parent entity type ID.
*/
public function __construct(array $definitions, $parent_entity_type_id) {
$this->definitions = $definitions;
$this->parentEntityTypeId = $parent_entity_type_id;
}
/**
* Gets the condition definitions.
*
* @return array
* The condition definitions.
*/
public function getDefinitions() {
return $this->definitions;
}
/**
* Sets the condition definitions.
*
* @param array $definitions
* The condition definitions.
*
* @return $this
*/
public function setDefinitions(array $definitions) {
$this->definitions = $definitions;
return $this;
}
/**
* Gets the parent entity type ID.
*
* @return string
* The parent entity type ID.
*/
public function getParentEntityTypeId() {
return $this->parentEntityTypeId;
}
}
......@@ -110,7 +110,8 @@ class ConditionsWidget extends WidgetBase implements ContainerFactoryPluginInter
'#title' => $this->t('Entity types'),
'#options' => $entity_types,
'#default_value' => $this->getSetting('entity_types'),
'#description' => $this->t('Only conditions matching the specified entity types will be displayed. Leave empty to show all.'),
'#description' => $this->t('Only conditions matching the specified entity types will be displayed.'),
'#required' => TRUE,
];
return $element;
......@@ -135,9 +136,6 @@ class ConditionsWidget extends WidgetBase implements ContainerFactoryPluginInter
$summary[] = $this->t('Entity types: @entity_types', ['@entity_types' => implode(', ', $entity_types)]);
}
else {
$summary[] = $this->t('No entity type restrictions');
}
return $summary;
}
......@@ -158,6 +156,7 @@ class ConditionsWidget extends WidgetBase implements ContainerFactoryPluginInter
'#type' => 'commerce_conditions',
'#title' => $this->fieldDefinition->getLabel(),
'#default_value' => $values,
'#parent_entity_type' => $this->fieldDefinition->getTargetEntityTypeId(),
'#entity_types' => array_filter($this->getSetting('entity_types')),
'#required' => $this->fieldDefinition->isRequired(),
];
......
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