Commit 5d20c57b authored by alexpott's avatar alexpott

Issue #2107243 by amateescu, jibran, larowlan, Xano: Decouple entity reference...

Issue #2107243 by amateescu, jibran, larowlan, Xano: Decouple entity reference selection plugins from field definitions
parent c0b346b7
......@@ -348,6 +348,9 @@ services:
arguments: ['@config.manager', '@entity.manager']
tags:
- { name: event_subscriber }
plugin.manager.entity_reference_selection:
class: Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
parent: default_plugin_manager
plugin.manager.block:
class: Drupal\Core\Block\BlockManager
parent: default_plugin_manager
......
......@@ -2,25 +2,25 @@
/**
* @file
* Contains \Drupal\entity_reference\Annotation\EntityReferenceSelection.
* Contains \Drupal\Core\Entity\Annotation\EntityReferenceSelection.
*/
namespace Drupal\entity_reference\Annotation;
namespace Drupal\Core\Entity\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines an EntityReferenceSelection plugin annotation object.
*
* Plugin Namespace: Plugin\entity_reference\selection
* Plugin Namespace: Plugin\EntityReferenceSelection
*
* For a working example, see
* \Drupal\comment\Plugin\entity_reference\selection\CommentSelection
* \Drupal\comment\Plugin\EntityReferenceSelection\CommentSelection
*
* @see \Drupal\entity_reference\Plugin\Type\SelectionPluginManager
* @see \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface
* @see \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase
* @see \Drupal\entity_reference\Plugin\Derivative\SelectionBase
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
* @see plugin_api
*
* @Annotation
......
......@@ -2,25 +2,25 @@
/**
* @file
* Contains \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface.
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface.
*/
namespace Drupal\entity_reference\Plugin\Type\Selection;
namespace Drupal\Core\Entity\EntityReferenceSelection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Interface definition for Entity Reference Selection plugins.
*
* @see \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase
* @see \Drupal\entity_reference\Plugin\Type\SelectionPluginManager
* @see \Drupal\entity_reference\Annotation\EntityReferenceSelection
* @see \Drupal\entity_reference\Plugin\Derivative\SelectionBase
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
* @see plugin_api
*/
interface SelectionInterface {
interface SelectionInterface extends PluginFormInterface {
/**
* Returns a list of referenceable entities.
......@@ -78,15 +78,4 @@ public function validateAutocompleteInput($input, &$element, FormStateInterface
*/
public function entityQueryAlter(SelectInterface $query);
/**
* Generates the settings form for this selection.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the selection is associated.
*
* @return array
* A Form API array.
*/
public static function settingsForm(FieldDefinitionInterface $field_definition);
}
......@@ -2,75 +2,68 @@
/**
* @file
* Contains \Drupal\entity_reference\Plugin\Type\SelectionPluginManager.
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager.
*/
namespace Drupal\entity_reference\Plugin\Type;
namespace Drupal\Core\Entity\EntityReferenceSelection;
use Drupal\Component\Plugin\Factory\ReflectionFactory;
use Drupal\Component\Plugin\FallbackPluginManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
/**
* Plugin type manager for Entity Reference Selection plugins.
*
* @see \Drupal\entity_reference\Annotation\EntityReferenceSelection
* @see \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface
* @see \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase
* @see \Drupal\entity_reference\Plugin\Derivative\SelectionBase
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
* @see plugin_api
*/
class SelectionPluginManager extends DefaultPluginManager implements FallbackPluginManagerInterface {
class SelectionPluginManager extends DefaultPluginManager implements SelectionPluginManagerInterface, FallbackPluginManagerInterface {
/**
* {@inheritdoc}
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
$this->discovery = new AnnotatedClassDiscovery('Plugin/entity_reference/selection', $namespaces, 'Drupal\entity_reference\Annotation\EntityReferenceSelection');
// We're not using the parent constructor because we use a different factory
// method and don't need the derivative discovery decorator.
$this->factory = new ReflectionFactory($this, '\Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface');
$this->moduleHandler = $module_handler;
$this->alterInfo('entity_reference_selection');
$this->setCacheBackend($cache_backend, 'entity_reference_selection_plugins');
parent::__construct('Plugin/EntityReferenceSelection', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface', 'Drupal\Core\Entity\Annotation\EntityReferenceSelection');
}
/**
* Overrides \Drupal\Component\Plugin\PluginManagerBase::getInstance().
* {@inheritdoc}
*/
public function getInstance(array $options) {
$selection_handler = $options['field_definition']->getSetting('handler');
$target_entity_type = $options['field_definition']->getSetting('target_type');
if (!isset($options['target_type'])) {
throw new \InvalidArgumentException("Missing required 'target_type' property for a EntityReferenceSelection plugin.");
}
// Initialize default options.
$options += array(
'handler' => 'default',
'handler_settings' => array(),
);
// Get all available selection plugins for this entity type.
$selection_handler_groups = $this->getSelectionGroups($target_entity_type);
$selection_handler_groups = $this->getSelectionGroups($options['target_type']);
// Sort the selection plugins by weight and select the best match.
uasort($selection_handler_groups[$selection_handler], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
end($selection_handler_groups[$selection_handler]);
$plugin_id = key($selection_handler_groups[$selection_handler]);
uasort($selection_handler_groups[$options['handler']], array('Drupal\Component\Utility\SortArray', 'sortByWeightElement'));
end($selection_handler_groups[$options['handler']]);
$plugin_id = key($selection_handler_groups[$options['handler']]);
return $this->createInstance($plugin_id, $options);
}
/**
* Returns a list of selection plugins that can reference a specific entity
* type.
*
* @param string $entity_type
* A Drupal entity type.
*
* @return array
* An array of selection plugins grouped by selection group.
* {@inheritdoc}
*/
public function getSelectionGroups($entity_type) {
public function getSelectionGroups($entity_type_id) {
$plugins = array();
$definitions = $this->getDefinitions();
......@@ -78,7 +71,7 @@ public function getSelectionGroups($entity_type) {
unset($definitions['broken']);
foreach ($definitions as $plugin_id => $plugin) {
if (empty($plugin['entity_types']) || in_array($entity_type, $plugin['entity_types'])) {
if (empty($plugin['entity_types']) || in_array($entity_type_id, $plugin['entity_types'])) {
$plugins[$plugin['group']][$plugin_id] = $plugin;
}
}
......@@ -87,19 +80,13 @@ public function getSelectionGroups($entity_type) {
}
/**
* Gets the selection handler for a given entity_reference field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition for the operation.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for the operation.
*
* @return \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface
* The selection plugin.
* {@inheritdoc}
*/
public function getSelectionHandler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$options = array(
'field_definition' => $field_definition,
'target_type' => $field_definition->getFieldStorageDefinition()->getSetting('target_type'),
'handler' => $field_definition->getSetting('handler'),
'handler_settings' => $field_definition->getSetting('handler_settings'),
'entity' => $entity,
);
return $this->getInstance($options);
......
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface.
*/
namespace Drupal\Core\Entity\EntityReferenceSelection;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
/**
* Defines an interface for the entity reference selection plugin manager.
*/
interface SelectionPluginManagerInterface extends PluginManagerInterface {
/**
* Returns selection plugins that can reference a specific entity type.
*
* @param string $entity_type_id
* A Drupal entity type ID.
*
* @return array
* An array of selection plugins grouped by selection group.
*/
public function getSelectionGroups($entity_type_id);
/**
* Gets the selection handler for a given entity_reference field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition for the operation.
* @param \Drupal\Core\Entity\EntityInterface $entity
* (optional) The entity for the operation. Defaults to NULL.
*
* @return \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* The selection plugin.
*/
public function getSelectionHandler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL);
}
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Plugin\Derivative\SelectionBase.
*/
namespace Drupal\Core\Entity\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides derivative plugins for Entity Reference Selection plugins.
*
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* @see plugin_api
*/
class SelectionBase extends DeriverBase implements ContainerDeriverInterface {
/**
* The entity manager
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Creates an SelectionBase object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;
$this->derivatives[$entity_type_id]['entity_types'] = array($entity_type_id);
$this->derivatives[$entity_type_id]['label'] = t('@entity_type selection', array('@entity_type' => $entity_type->getLabel()));
$this->derivatives[$entity_type_id]['base_plugin_label'] = (string) $base_plugin_definition['label'];
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
}
......@@ -2,12 +2,13 @@
/**
* @file
* Contains \Drupal\entity_reference\Plugin\Type\Selection\SelectionBroken.
* Contains \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBroken.
*/
namespace Drupal\entity_reference\Plugin\Type\Selection;
namespace Drupal\Core\Entity\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
......@@ -19,18 +20,28 @@
* label = @Translation("Broken/Missing")
* )
*/
class SelectionBroken implements SelectionInterface {
class Broken implements SelectionInterface {
/**
* {@inheritdoc}
*/
public static function settingsForm(FieldDefinitionInterface $field_definition) {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['selection_handler'] = array(
'#markup' => t('The selected selection handler is broken.'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
......
......@@ -2,18 +2,22 @@
/**
* @file
* Contains \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase.
* Contains \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase.
*/
namespace Drupal\entity_reference\Plugin\entity_reference\selection;
namespace Drupal\Core\Entity\Plugin\EntityReferenceSelection;
use Drupal\Component\Utility\String;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Default plugin implementation of the Entity Reference Selection plugin.
......@@ -21,10 +25,10 @@
* Also serves as a base class for specific types of Entity Reference
* Selection plugins.
*
* @see \Drupal\entity_reference\Plugin\Type\SelectionPluginManager
* @see \Drupal\entity_reference\Annotation\EntityReferenceSelection
* @see \Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface
* @see \Drupal\entity_reference\Plugin\Derivative\SelectionBase
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
* @see plugin_api
*
* @EntityReferenceSelection(
......@@ -32,42 +36,78 @@
* label = @Translation("Default"),
* group = "default",
* weight = 0,
* deriver = "Drupal\entity_reference\Plugin\Derivative\SelectionBase"
* deriver = "Drupal\Core\Entity\Plugin\Derivative\SelectionBase"
* )
*/
class SelectionBase implements SelectionInterface {
class SelectionBase extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
/**
* The field definition.
* The entity manager.
*
* @var \Drupal\Core\Field\FieldDefinitionInterface
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $fieldDefinition;
protected $entityManager;
/**
* The entity object, or NULL
* The module handler service.
*
* @var \Drupal\Core\Entity\EntityInterface|null
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $entity;
protected $moduleHandler;
/**
* Constructs a SelectionBase object.
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new SelectionBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$this->fieldDefinition = $field_definition;
$this->entity = $entity;
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
$this->moduleHandler = $module_handler;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function settingsForm(FieldDefinitionInterface $field_definition) {
$entity_manager = \Drupal::entityManager();
$entity_type_id = $field_definition->getSetting('target_type');
$selection_handler_settings = $field_definition->getSetting('handler_settings') ?: array();
$entity_type = $entity_manager->getDefinition($entity_type_id);
$bundles = $entity_manager->getBundleInfo($entity_type_id);
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager'),
$container->get('module_handler'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$entity_type_id = $this->configuration['target_type'];
$selection_handler_settings = $this->configuration['handler_settings'];
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$bundles = $this->entityManager->getBundleInfo($entity_type_id);
// Merge-in default values.
$selection_handler_settings += array(
......@@ -84,13 +124,13 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
$bundle_options[$bundle_name] = $bundle_info['label'];
}
$target_bundles_title = t('Bundles');
$target_bundles_title = $this->t('Bundles');
// Default core entity types with sensible labels.
if ($entity_type_id == 'node') {
$target_bundles_title = t('Content types');
$target_bundles_title = $this->t('Content types');
}
elseif ($entity_type_id == 'taxonomy_term') {
$target_bundles_title = t('Vocabularies');
$target_bundles_title = $this->t('Vocabularies');
}
$form['target_bundles'] = array(
......@@ -114,7 +154,7 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
$fields = array();
foreach (array_keys($bundles) as $bundle) {
$bundle_fields = array_filter($entity_manager->getFieldDefinitions($entity_type_id, $bundle), function ($field_definition) {
$bundle_fields = array_filter($this->entityManager->getFieldDefinitions($entity_type_id, $bundle), function ($field_definition) {
return !$field_definition->isComputed();
});
foreach ($bundle_fields as $field_name => $field_definition) {
......@@ -125,20 +165,20 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
// @todo: Use property labels instead of the column name.
if (count($columns) > 1) {
foreach ($columns as $column_name => $column_info) {
$fields[$field_name . '.' . $column_name] = t('@label (@column)', array('@label' => $field_definition->getLabel(), '@column' => $column_name));
$fields[$field_name . '.' . $column_name] = $this->t('@label (@column)', array('@label' => $field_definition->getLabel(), '@column' => $column_name));
}
}
else {
$fields[$field_name] = t('@label', array('@label' => $field_definition->getLabel()));
$fields[$field_name] = $this->t('@label', array('@label' => $field_definition->getLabel()));
}
}
}
$form['sort']['field'] = array(
'#type' => 'select',
'#title' => t('Sort by'),
'#title' => $this->t('Sort by'),
'#options' => array(
'_none' => t('- None -'),
'_none' => $this->t('- None -'),
) + $fields,
'#ajax' => TRUE,
'#limit_validation_errors' => array(),
......@@ -159,11 +199,11 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
$form['sort']['settings']['direction'] = array(
'#type' => 'select',
'#title' => t('Sort direction'),
'#title' => $this->t('Sort direction'),
'#required' => TRUE,
'#options' => array(
'ASC' => t('Ascending'),
'DESC' => t('Descending'),
'ASC' => $this->t('Ascending'),
'DESC' => $this->t('Descending'),
),
'#default_value' => $selection_handler_settings['sort']['direction'],
);
......@@ -173,11 +213,21 @@ public static function settingsForm(FieldDefinitionInterface $field_definition)
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }