Issue #2107243 by amateescu, jibran, larowlan, Xano: Decouple entity reference selection plugins from field definitions

8.0.x
Alex Pott 2015-01-23 13:02:31 +00:00
parent c0b346b75c
commit 5d20c57be1
28 changed files with 639 additions and 475 deletions

View File

@ -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

View File

@ -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

View File

@ -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 @@ interface SelectionInterface {
*/
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);
}

View File

@ -0,0 +1,102 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager.
*/
namespace Drupal\Core\Entity\EntityReferenceSelection;
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;
/**
* Plugin type manager for Entity Reference Selection plugins.
*
* @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 SelectionPluginManagerInterface, FallbackPluginManagerInterface {
/**
* {@inheritdoc}
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $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');
}
/**
* {@inheritdoc}
*/
public function getInstance(array $options) {
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($options['target_type']);
// Sort the selection plugins by weight and select the best match.
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);
}
/**
* {@inheritdoc}
*/
public function getSelectionGroups($entity_type_id) {
$plugins = array();
$definitions = $this->getDefinitions();
// Do not display the 'broken' plugin in the UI.
unset($definitions['broken']);
foreach ($definitions as $plugin_id => $plugin) {
if (empty($plugin['entity_types']) || in_array($entity_type_id, $plugin['entity_types'])) {
$plugins[$plugin['group']][$plugin_id] = $plugin;
}
}
return $plugins;
}
/**
* {@inheritdoc}
*/
public function getSelectionHandler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$options = array(
'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);
}
/**
* {@inheritdoc}
*/
public function getFallbackPluginId($plugin_id, array $configuration = array()) {
return 'broken';
}
}

View File

@ -0,0 +1,43 @@
<?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);
}

View File

@ -0,0 +1,65 @@
<?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);
}
}

View File

@ -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 @@ use Drupal\Core\Form\FormStateInterface;
* 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}
*/

View File

@ -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 @@ use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface;
* 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 @@ use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface;
* 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
*/
public function __construct(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$this->fieldDefinition = $field_definition;
$this->entity = $entity;
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(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 @@ class SelectionBase implements SelectionInterface {
$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 @@ class SelectionBase implements SelectionInterface {
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 @@ class SelectionBase implements SelectionInterface {
// @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 @@ class SelectionBase implements SelectionInterface {
$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 @@ class SelectionBase implements SelectionInterface {
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$target_type = $this->fieldDefinition->getSetting('target_type');
$target_type = $this->configuration['target_type'];
$query = $this->buildEntityQuery($match, $match_operator);
if ($limit > 0) {
@ -216,8 +266,8 @@ class SelectionBase implements SelectionInterface {
public function validateReferenceableEntities(array $ids) {
$result = array();
if ($ids) {
$target_type = $this->fieldDefinition->getSetting('target_type');
$entity_type = \Drupal::entityManager()->getDefinition($target_type);
$target_type = $this->configuration['target_type'];
$entity_type = $this->entityManager->getDefinition($target_type);
$query = $this->buildEntityQuery();
$result = $query
->condition($entity_type->getKey('id'), $ids, 'IN')
@ -243,13 +293,13 @@ class SelectionBase implements SelectionInterface {
if (empty($entities)) {
if ($strict) {
// Error if there are no entities available for a required field.
$form_state->setError($element, t('There are no entities matching "%value".', $params));
$form_state->setError($element, $this->t('There are no entities matching "%value".', $params));
}
}
elseif (count($entities) > 5) {
$params['@id'] = key($entities);
// Error if there are more than 5 matching entities.
$form_state->setError($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params));
$form_state->setError($element, $this->t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params));
}
elseif (count($entities) > 1) {
// More helpful error if there are only a few matching entities.
@ -258,7 +308,7 @@ class SelectionBase implements SelectionInterface {
$multiples[] = $name . ' (' . $id . ')';
}
$params['@id'] = $id;
$form_state->setError($element, t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array('%multiple' => implode('", "', $multiples))));
$form_state->setError($element, $this->t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array('%multiple' => implode('", "', $multiples))));
}
else {
// Take the one and only matching entity.
@ -280,11 +330,11 @@ class SelectionBase implements SelectionInterface {
* it.
*/
public function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$target_type = $this->fieldDefinition->getSetting('target_type');
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$entity_type = \Drupal::entityManager()->getDefinition($target_type);
$target_type = $this->configuration['target_type'];
$handler_settings = $this->configuration['handler_settings'];
$entity_type = $this->entityManager->getDefinition($target_type);
$query = \Drupal::entityQuery($target_type);
$query = $this->entityManager->getStorage($target_type)->getQuery();
if (!empty($handler_settings['target_bundles'])) {
$query->condition($entity_type->getKey('bundle'), $handler_settings['target_bundles'], 'IN');
}
@ -294,16 +344,14 @@ class SelectionBase implements SelectionInterface {
}
// Add entity-access tag.
$query->addTag($this->fieldDefinition->getSetting('target_type') . '_access');
$query->addTag($target_type . '_access');
// Add the Selection handler for
// entity_reference_query_entity_reference_alter().
$query->addTag('entity_reference');
$query->addMetaData('field_definition', $this->fieldDefinition);
$query->addMetaData('entity_reference_selection_handler', $this);
// Add the sort option.
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
if (!empty($handler_settings['sort'])) {
$sort_settings = $handler_settings['sort'];
if ($sort_settings['field'] != '_none') {
@ -333,10 +381,11 @@ class SelectionBase implements SelectionInterface {
$query->alterTags = array($tag => TRUE);
$query->alterMetaData['base_table'] = $base_table;
\Drupal::moduleHandler()->alter(array('query', 'query_' . $tag), $query);
$this->moduleHandler->alter(array('query', 'query_' . $tag), $query);
// Restore the tags and metadata.
$query->alterTags = $old_tags;
$query->alterMetaData = $old_metadata;
}
}

View File

@ -243,7 +243,7 @@ class EntityReferenceItem extends FieldItemBase {
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$manager = \Drupal::service('plugin.manager.entity_reference.selection');
$manager = \Drupal::service('plugin.manager.entity_reference_selection');
if ($referenceable = $manager->getSelectionHandler($field_definition)->getReferenceableEntities()) {
$group = array_rand($referenceable);
$values['target_id'] = array_rand($referenceable[$group]);

View File

@ -2,20 +2,20 @@
/**
* @file
* Contains \Drupal\comment\Plugin\entity_reference\selection\CommentSelection.
* Contains \Drupal\comment\Plugin\EntityReferenceSelection\CommentSelection.
*/
namespace Drupal\comment\Plugin\entity_reference\selection;
namespace Drupal\comment\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
use Drupal\comment\CommentInterface;
use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
/**
* Provides specific access control for the comment entity type.
*
* @EntityReferenceSelection(
* id = "comment_default",
* id = "default:comment",
* label = @Translation("Comment selection"),
* entity_types = {"comment"},
* group = "default",
@ -33,7 +33,7 @@ class CommentSelection extends SelectionBase {
// Adding the 'comment_access' tag is sadly insufficient for comments:
// core requires us to also know about the concept of 'published' and
// 'unpublished'.
if (!\Drupal::currentUser()->hasPermission('administer comments')) {
if (!$this->currentUser->hasPermission('administer comments')) {
$query->condition('status', CommentInterface::PUBLISHED);
}
return $query;
@ -61,8 +61,9 @@ class CommentSelection extends SelectionBase {
// Passing the query to node_query_node_access_alter() is sadly
// insufficient for nodes.
// @see SelectionEntityTypeNode::entityQueryAlter()
if (!\Drupal::currentUser()->hasPermission('bypass node access') && !count(\Drupal::moduleHandler()->getImplementations('node_grants'))) {
if (!$this->currentUser->hasPermission('bypass node access') && !count($this->moduleHandler->getImplementations('node_grants'))) {
$query->condition($node_alias . '.status', 1);
}
}
}

View File

@ -201,7 +201,7 @@ function entity_reference_query_entity_reference_alter(AlterableInterface $query
* @param int $cardinality
* The cardinality of the field.
*
* @see \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase::settingsForm()
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
*/
function entity_reference_create_field($entity_type, $bundle, $field_name, $field_label, $target_entity_type, $selection_handler = 'default', $selection_handler_settings = array(), $cardinality = 1) {
// Look for or add the specified field to the requested entity bundle.

View File

@ -1,7 +1,4 @@
services:
plugin.manager.entity_reference.selection:
class: Drupal\entity_reference\Plugin\Type\SelectionPluginManager
parent: default_plugin_manager
entity_reference.autocomplete:
class: Drupal\entity_reference\EntityReferenceAutocomplete
arguments: ['@entity.manager', '@plugin.manager.entity_reference.selection']
arguments: ['@entity.manager', '@plugin.manager.entity_reference_selection']

View File

@ -80,7 +80,7 @@ class ConfigurableEntityReferenceItem extends EntityReferenceItem implements Opt
*/
public function getSettableOptions(AccountInterface $account = NULL) {
$field_definition = $this->getFieldDefinition();
if (!$options = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($field_definition, $this->getEntity())->getReferenceableEntities()) {
if (!$options = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field_definition, $this->getEntity())->getReferenceableEntities()) {
return array();
}
@ -138,17 +138,18 @@ class ConfigurableEntityReferenceItem extends EntityReferenceItem implements Opt
$field = $form_state->get('field');
// Get all selection plugins for this entity type.
$selection_plugins = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionGroups($this->getSetting('target_type'));
$handler_groups = array_keys($selection_plugins);
$handlers = \Drupal::service('plugin.manager.entity_reference.selection')->getDefinitions();
$selection_plugins = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionGroups($this->getSetting('target_type'));
$handlers_options = array();
foreach ($handlers as $plugin_id => $plugin) {
foreach (array_keys($selection_plugins) as $selection_group_id) {
// We only display base plugins (e.g. 'default', 'views', ...) and not
// entity type specific plugins (e.g. 'default_node', 'default_user',
// entity type specific plugins (e.g. 'default:node', 'default:user',
// ...).
if (in_array($plugin_id, $handler_groups)) {
$handlers_options[$plugin_id] = String::checkPlain($plugin['label']);
if (array_key_exists($selection_group_id, $selection_plugins[$selection_group_id])) {
$handlers_options[$selection_group_id] = String::checkPlain($selection_plugins[$selection_group_id][$selection_group_id]['label']);
}
elseif (array_key_exists($selection_group_id . ':' . $this->getSetting('target_type'), $selection_plugins[$selection_group_id])) {
$selection_group_plugin = $selection_group_id . ':' . $this->getSetting('target_type');
$handlers_options[$selection_group_id] = String::checkPlain($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
}
}
@ -191,8 +192,8 @@ class ConfigurableEntityReferenceItem extends EntityReferenceItem implements Opt
'#attributes' => array('class' => array('entity_reference-settings')),
);
$handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($field);
$form['handler']['handler_settings'] += $handler->settingsForm($field);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
$form['handler']['handler_settings'] += $handler->buildConfigurationForm(array(), $form_state);
return $form;
}
@ -209,6 +210,9 @@ class ConfigurableEntityReferenceItem extends EntityReferenceItem implements Opt
if ($form_state->hasValue('field')) {
$form_state->unsetValue(array('field', 'settings', 'handler_submit'));
$form_state->get('field')->settings = $form_state->getValue(['field', 'settings']);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($form_state->get('field'));
$handler->validateConfigurationForm($form, $form_state);
}
}

View File

@ -10,9 +10,8 @@ namespace Drupal\entity_reference;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\entity_reference\Plugin\Type\SelectionPluginManager;
use Drupal\field\FieldConfigInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
@ -30,7 +29,7 @@ class EntityReferenceAutocomplete {
/**
* The Entity reference selection handler plugin manager.
*
* @var \Drupal\entity_reference\Plugin\Type\SelectionPluginManager
* @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
*/
protected $selectionHandlerManager;
@ -39,10 +38,10 @@ class EntityReferenceAutocomplete {
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\entity_reference\Plugin\Type\SelectionPluginManager $selection_manager
* @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager
* The Entity reference selection handler plugin manager.
*/
public function __construct(EntityManagerInterface $entity_manager, SelectionPluginManager $selection_manager) {
public function __construct(EntityManagerInterface $entity_manager, SelectionPluginManagerInterface $selection_manager) {
$this->entityManager = $entity_manager;
$this->selectionHandlerManager = $selection_manager;
}

View File

@ -1,41 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\entity_reference\Plugin\Derivative\SelectionBase.
*/
namespace Drupal\entity_reference\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
/**
* Provides derivative plugins 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\Type\Selection\SelectionInterface
* @see plugin_api
*/
class SelectionBase extends DeriverBase {
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$supported_entities = array(
'comment',
'file',
'node',
'taxonomy_term',
'user'
);
foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
if (!in_array($entity_type_id, $supported_entities)) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;
$this->derivatives[$entity_type_id]['label'] = t('@entity_type selection', array('@entity_type' => $entity_type->getLabel()));
}
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
}

View File

@ -44,7 +44,7 @@ class AutocompleteTagsWidget extends AutocompleteWidgetBase {
public function elementValidate($element, FormStateInterface $form_state, $form) {
$value = array();
// If a value was entered into the autocomplete.
$handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($this->fieldDefinition);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($this->fieldDefinition);
$bundles = entity_get_bundles($this->getFieldSetting('target_type'));
$auto_create = $this->getSelectionHandlerSetting('auto_create');

View File

@ -64,7 +64,7 @@ class AutocompleteWidget extends AutocompleteWidgetBase {
if ($value === NULL) {
// Try to get a match from the input string when the user didn't use the
// autocomplete but filled in a value manually.
$handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($this->fieldDefinition);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($this->fieldDefinition);
$value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form, !$auto_create);
}

View File

@ -1,115 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\entity_reference\Plugin\Type\SelectionPluginManager.
*/
namespace Drupal\entity_reference\Plugin\Type;
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 plugin_api
*/
class SelectionPluginManager extends DefaultPluginManager implements 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');
}
/**
* Overrides \Drupal\Component\Plugin\PluginManagerBase::getInstance().
*/
public function getInstance(array $options) {
$selection_handler = $options['field_definition']->getSetting('handler');
$target_entity_type = $options['field_definition']->getSetting('target_type');
// Get all available selection plugins for this entity type.
$selection_handler_groups = $this->getSelectionGroups($target_entity_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]);
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.
*/
public function getSelectionGroups($entity_type) {
$plugins = array();
$definitions = $this->getDefinitions();
// Do not display the 'broken' plugin in the UI.
unset($definitions['broken']);
foreach ($definitions as $plugin_id => $plugin) {
if (empty($plugin['entity_types']) || in_array($entity_type, $plugin['entity_types'])) {
$plugins[$plugin['group']][$plugin_id] = $plugin;
}
}
return $plugins;
}
/**
* 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.
*/
public function getSelectionHandler(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
$options = array(
'field_definition' => $field_definition,
'entity' => $entity,
);
return $this->getInstance($options);
}
/**
* {@inheritdoc}
*/
public function getFallbackPluginId($plugin_id, array $configuration = array()) {
return 'broken';
}
}

View File

@ -118,6 +118,26 @@ class EntityReferenceAdminTest extends WebTestBase {
// The first 'Edit' link is for the Body field.
$this->clickLink(t('Edit'), 1);
$this->drupalPostForm(NULL, array(), t('Save settings'));
// Switch the target type to 'taxonomy_term' and check that the settings
// specific to its selection handler are displayed.
$field_name = 'node.' . $this->type . '.field_test';
$edit = array(
'field_storage[settings][target_type]' => 'taxonomy_term',
);
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
$this->drupalGet($bundle_path . '/fields/' . $field_name);
$this->assertFieldByName('field[settings][handler_settings][auto_create]');
// Switch the target type to 'user' and check that the settings specific to
// its selection handler are displayed.
$field_name = 'node.' . $this->type . '.field_test';
$edit = array(
'field_storage[settings][target_type]' => 'user',
);
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
$this->drupalGet($bundle_path . '/fields/' . $field_name);
$this->assertFieldByName('field[settings][handler_settings][filter][type]', '_none');
}

View File

@ -103,7 +103,7 @@ class SelectionTest extends WebTestBase {
*/
public function testSelectionHandler() {
// Get values from selection handler.
$handler = $this->container->get('plugin.manager.entity_reference.selection')->getSelectionHandler($this->field);
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($this->field);
$result = $handler->getReferenceableEntities();
$this->assertResults($result);
}
@ -140,7 +140,7 @@ class SelectionTest extends WebTestBase {
$view->save();
// Get values from the selection handler.
$handler = $this->container->get('plugin.manager.entity_reference.selection')->getSelectionHandler($this->field);
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($this->field);
$result = $handler->getReferenceableEntities();
$this->assertResults($result);
}

View File

@ -2,18 +2,18 @@
/**
* @file
* Contains \Drupal\file\Plugin\Type\selection\FileSelection.
* Contains \Drupal\file\Plugin\EntityReferenceSelection\FileSelection.
*/
namespace Drupal\file\Plugin\entity_reference\selection;
namespace Drupal\file\Plugin\EntityReferenceSelection;
use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
/**
* Provides specific access control for the file entity type.
*
* @EntityReferenceSelection(
* id = "file_default",
* id = "default:file",
* label = @Translation("File selection"),
* entity_types = {"file"},
* group = "default",
@ -30,4 +30,5 @@ class FileSelection extends SelectionBase {
$query->condition('status', FILE_STATUS_PERMANENT);
return $query;
}
}

View File

@ -2,18 +2,18 @@
/**
* @file
* Contains \Drupal\node\Plugin\Type\selection\NodeSelection.
* Contains \Drupal\node\Plugin\EntityReferenceSelection\NodeSelection.
*/
namespace Drupal\node\Plugin\entity_reference\selection;
namespace Drupal\node\Plugin\EntityReferenceSelection;
use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
/**
* Provides specific access control for the node entity type.
*
* @EntityReferenceSelection(
* id = "node_default",
* id = "default:node",
* label = @Translation("Node selection"),
* entity_types = {"node"},
* group = "default",
@ -32,9 +32,10 @@ class NodeSelection extends SelectionBase {
// 'unpublished'. We need to do that as long as there are no access control
// modules in use on the site. As long as one access control module is there,
// it is supposed to handle this check.
if (!\Drupal::currentUser()->hasPermission('bypass node access') && !count(\Drupal::moduleHandler()->getImplementations('node_grants'))) {
if (!$this->currentUser->hasPermission('bypass node access') && !count($this->moduleHandler->getImplementations('node_grants'))) {
$query->condition('status', NODE_PUBLISHED);
}
return $query;
}
}

View File

@ -2,13 +2,12 @@
/**
* @file
* Contains \Drupal\entity_reference\Tests\EntityReferenceSelectionAccessTest.
* Contains \Drupal\system\Tests\Entity\EntityReferenceSelection\EntityReferenceSelectionAccessTest.
*/
namespace Drupal\entity_reference\Tests;
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
use Drupal\Component\Utility\String;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\comment\CommentInterface;
use Drupal\simpletest\WebTestBase;
@ -20,7 +19,12 @@ use Drupal\simpletest\WebTestBase;
*/
class EntityReferenceSelectionAccessTest extends WebTestBase {
public static $modules = array('node', 'comment', 'entity_reference', 'entity_test');
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'comment');
protected function setUp() {
parent::setUp();
@ -29,8 +33,18 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
}
protected function assertReferenceable(FieldDefinitionInterface $field_definition, $tests, $handler_name) {
$handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($field_definition);
/**
* Checks that a selection plugin returns the expected results.
*
* @param array $selection_options
* An array of options as required by entity reference selection plugins.
* @param array $tests
* An array of tests to run.
* @param string $handler_name
* The name of the entity type selection handler being tested.
*/
protected function assertReferenceable(array $selection_options, $tests, $handler_name) {
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($selection_options);
foreach ($tests as $test) {
foreach ($test['arguments'] as $arguments) {
@ -55,30 +69,13 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
* Test the node-specific overrides of the entity handler.
*/
public function testNodeHandler() {
// Create a field.
$field_storage = entity_create('field_storage_config', array(
'field_name' => 'test_field',
'entity_type' => 'entity_test',
'translatable' => FALSE,
'entity_types' => array(),
'settings' => array(
'target_type' => 'node',
$selection_options = array(
'target_type' => 'node',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
'type' => 'entity_reference',
'cardinality' => '1',
));
$field_storage->save();
$field = entity_create('field_config', array(
'field_storage' => $field_storage,
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
),
));
$field->save();
);
// Build a set of test data.
// Titles contain HTML-special characters to test escaping.
@ -162,7 +159,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
'result' => array(),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'Node handler');
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler');
// Test as an admin.
$admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
@ -191,36 +188,20 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'Node handler (admin)');
$this->assertReferenceable($selection_options, $referenceable_tests, 'Node handler (admin)');
}
/**
* Test the user-specific overrides of the entity handler.
*/
public function testUserHandler() {
// Create a field.
$field_storage = entity_create('field_storage_config', array(
'field_name' => 'test_field',
'entity_type' => 'entity_test',
'translatable' => FALSE,
'settings' => array(
'target_type' => 'user',
$selection_options = array(
'target_type' => 'user',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
'type' => 'entity_reference',
'cardinality' => '1',
));
$field_storage->save();
$field = entity_create('field_config', array(
'field_storage' => $field_storage,
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
),
));
$field->save();
);
// Build a set of test data.
$user_values = array(
@ -296,7 +277,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
'result' => array(),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'User handler');
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler');
\Drupal::currentUser()->setAccount($users['admin']);
$referenceable_tests = array(
@ -335,37 +316,20 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'User handler (admin)');
$this->assertReferenceable($selection_options, $referenceable_tests, 'User handler (admin)');
}
/**
* Test the comment-specific overrides of the entity handler.
*/
public function testCommentHandler() {
// Create a field.
$field_storage = entity_create('field_storage_config', array(
'field_name' => 'test_field',
'entity_type' => 'entity_test',
'translatable' => FALSE,
'entity_types' => array(),
'settings' => array(
'target_type' => 'comment',
$selection_options = array(
'target_type' => 'comment',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
'type' => 'entity_reference',
'cardinality' => '1',
));
$field_storage->save();
$field = entity_create('field_config', array(
'field_storage' => $field_storage,
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
),
),
));
$field->save();
);
// Build a set of test data.
$node_values = array(
@ -474,7 +438,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
'result' => array(),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'Comment handler');
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler');
// Test as a comment admin.
$admin_user = $this->drupalCreateUser(array('access content', 'access comments', 'administer comments'));
@ -492,7 +456,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'Comment handler (comment admin)');
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment admin)');
// Test as a node and comment admin.
$admin_user = $this->drupalCreateUser(array('access content', 'access comments', 'administer comments', 'bypass node access'));
@ -511,6 +475,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
),
),
);
$this->assertReferenceable($field, $referenceable_tests, 'Comment handler (comment + node admin)');
$this->assertReferenceable($selection_options, $referenceable_tests, 'Comment handler (comment + node admin)');
}
}

View File

@ -2,28 +2,42 @@
/**
* @file
* Contains \Drupal\entity_reference\Tests\EntityReferenceSelectionSortTest.
* Contains \Drupal\system\Tests\Entity\EntityReferenceSelection\EntityReferenceSelectionSortTest.
*/
namespace Drupal\entity_reference\Tests;
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
use Drupal\Component\Utility\String;
use Drupal\simpletest\WebTestBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
/**
* Tests sorting referenced items.
*
* @group entity_reference
*/
class EntityReferenceSelectionSortTest extends WebTestBase {
class EntityReferenceSelectionSortTest extends EntityUnitTestBase {
public static $modules = array('node', 'entity_reference', 'entity_test');
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node');
protected function setUp() {
parent::setUp();
// Create an Article node type.
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
$article = NodeType::create(array(
'type' => 'article',
));
$article->save();
// Test as a non-admin.
$normal_user = $this->createUser(array(), array('access content'));
\Drupal::currentUser()->setAccount($normal_user);
}
/**
@ -47,37 +61,6 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
'required' => FALSE,
))->save();
// Create a field.
$field_storage = entity_create('field_storage_config', array(
'field_name' => 'test_field',
'entity_type' => 'entity_test',
'translatable' => FALSE,
'settings' => array(
'target_type' => 'node',
),
'type' => 'entity_reference',
'cardinality' => 1,
));
$field_storage->save();
$field = entity_create('field_config', array(
'field_storage' => $field_storage,
'entity_type' => 'entity_test',
'bundle' => 'test_bundle',
'settings' => array(
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
// Add sorting.
'sort' => array(
'field' => 'field_text.value',
'direction' => 'DESC',
),
),
),
));
$field->save();
// Build a set of test data.
$node_values = array(
'published1' => array(
@ -107,17 +90,25 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
$nodes = array();
$node_labels = array();
foreach ($node_values as $key => $values) {
$node = entity_create('node', $values);
$node = Node::create($values);
$node->save();
$nodes[$key] = $node;
$node_labels[$key] = String::checkPlain($node->label());
}
// Test as a non-admin.
$normal_user = $this->drupalCreateUser(array('access content'));
\Drupal::currentUser()->setAccount($normal_user);
$handler = $this->container->get('plugin.manager.entity_reference.selection')->getSelectionHandler($field);
$selection_options = array(
'target_type' => 'node',
'handler' => 'default',
'handler_settings' => array(
'target_bundles' => array(),
// Add sorting.
'sort' => array(
'field' => 'field_text.value',
'direction' => 'DESC',
),
),
);
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
// Not only assert the result, but make sure the keys are sorted as
// expected.
@ -128,12 +119,12 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
);
$this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.');
// Assert sort by property.
$field->settings['handler_settings']['sort'] = array(
// Assert sort by base field.
$selection_options['handler_settings']['sort'] = array(
'field' => 'nid',
'direction' => 'ASC',
);
$handler = $this->container->get('plugin.manager.entity_reference.selection')->getSelectionHandler($field);
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
$result = $handler->getReferenceableEntities();
$expected_result = array(
$nodes['published1']->id() => $node_labels['published1'],
@ -141,4 +132,5 @@ class EntityReferenceSelectionSortTest extends WebTestBase {
);
$this->assertIdentical($result['article'], $expected_result, 'Query sorted by property returned expected values.');
}
}

View File

@ -2,22 +2,22 @@
/**
* @file
* Contains \Drupal\taxonomy\Plugin\entity_reference\selection\TermSelection.
* Contains \Drupal\taxonomy\Plugin\EntityReferenceSelection\TermSelection.
*/
namespace Drupal\taxonomy\Plugin\entity_reference\selection;
namespace Drupal\taxonomy\Plugin\EntityReferenceSelection;
use Drupal\Component\Utility\String;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Provides specific access control for the taxonomy_term entity type.
*
* @EntityReferenceSelection(
* id = "taxonomy_term_default",
* id = "default:taxonomy_term",
* label = @Translation("Taxonomy Term selection"),
* entity_types = {"taxonomy_term"},
* group = "default",
@ -36,15 +36,14 @@ class TermSelection extends SelectionBase {
/**
* {@inheritdoc}
*/
public static function settingsForm(FieldDefinitionInterface $field_definition) {
$form = parent::settingsForm($field_definition);
$selection_handler_settings = $field_definition->getSetting('handler_settings');
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
// @todo: Currently allow auto-create only on taxonomy terms.
$form['auto_create'] = array(
'#type' => 'checkbox',
'#title' => t("Create referenced entities if they don't already exist"),
'#default_value' => isset($selection_handler_settings['auto_create']) ? $selection_handler_settings['auto_create'] : FALSE,
'#title' => $this->t("Create referenced entities if they don't already exist"),
'#default_value' => isset($this->configuration['handler_settings']['auto_create']) ? $this->configuration['handler_settings']['auto_create'] : FALSE,
);
return $form;
@ -60,13 +59,13 @@ class TermSelection extends SelectionBase {
$options = array();
$bundles = entity_get_bundles('taxonomy_term');
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$bundles = $this->entityManager->getBundleInfo('taxonomy_term');
$handler_settings = $this->configuration['handler_settings'];
$bundle_names = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : array_keys($bundles);
foreach ($bundle_names as $bundle) {
if ($vocabulary = Vocabulary::load($bundle)) {
if ($terms = taxonomy_get_tree($vocabulary->id(), 0, NULL, TRUE)) {
if ($terms = $this->entityManager->getStorage('taxonomy_term')->loadTree($vocabulary->id(), 0, NULL, TRUE)) {
foreach ($terms as $term) {
$options[$vocabulary->id()][$term->id()] = str_repeat('-', $term->depth) . String::checkPlain($term->getName());
}
@ -76,4 +75,5 @@ class TermSelection extends SelectionBase {
return $options;
}
}

View File

@ -69,7 +69,7 @@ class TermEntityReferenceTest extends TaxonomyTestBase {
));
$field->save();
$handler = $this->container->get('plugin.manager.entity_reference.selection')->getSelectionHandler($field);
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
$result = $handler->getReferenceableEntities();
$expected_result = array(

View File

@ -2,21 +2,25 @@
/**
* @file
* Contains \Drupal\user\Plugin\entity_reference\selection\UserSelection.
* Contains \Drupal\user\Plugin\EntityReferenceSelection\UserSelection.
*/
namespace Drupal\user\Plugin\entity_reference\selection;
namespace Drupal\user\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides specific access control for the user entity type.
*
* @EntityReferenceSelection(
* id = "user_default",
* id = "default:user",
* label = @Translation("User selection"),
* entity_types = {"user"},
* group = "default",
@ -25,11 +29,57 @@ use Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase;
*/
class UserSelection extends SelectionBase {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Constructs a new UserSelection 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.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user, Connection $connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager, $module_handler, $current_user);
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public static function settingsForm(FieldDefinitionInterface $field_definition) {
$selection_handler_settings = $field_definition->getSetting('handler_settings');
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'),
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$selection_handler_settings = $this->configuration['handler_settings'];
// Merge in default values.
$selection_handler_settings += array(
@ -41,10 +91,10 @@ class UserSelection extends SelectionBase {
// Add user specific filter options.
$form['filter']['type'] = array(
'#type' => 'select',
'#title' => t('Filter by'),
'#title' => $this->t('Filter by'),
'#options' => array(
'_none' => t('- None -'),
'role' => t('User role'),
'_none' => $this->t('- None -'),
'role' => $this->t('User role'),
),
'#ajax' => TRUE,
'#limit_validation_errors' => array(),
@ -65,14 +115,14 @@ class UserSelection extends SelectionBase {
$form['filter']['settings']['role'] = array(
'#type' => 'checkboxes',
'#title' => t('Restrict to the selected roles'),
'#title' => $this->t('Restrict to the selected roles'),
'#required' => TRUE,
'#options' => array_diff_key(user_role_names(TRUE), array(DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID)),
'#default_value' => $selection_handler_settings['filter']['role'],
);
}
$form += parent::settingsForm($field_definition);
$form += parent::buildConfigurationForm($form, $form_state);
return $form;
}
@ -89,14 +139,14 @@ class UserSelection extends SelectionBase {
}
// Filter by role.
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$handler_settings = $this->configuration['handler_settings'];
if (!empty($handler_settings['filter']['role'])) {
$query->condition('roles', $handler_settings['filter']['role'], 'IN');
}
// Adding the permission check is sadly insufficient for users: core
// requires us to also know about the concept of 'blocked' and 'active'.
if (!\Drupal::currentUser()->hasPermission('administer users')) {
if (!$this->currentUser->hasPermission('administer users')) {
$query->condition('status', 1);
}
return $query;
@ -106,7 +156,7 @@ class UserSelection extends SelectionBase {
* {@inheritdoc}
*/
public function entityQueryAlter(SelectInterface $query) {
if (\Drupal::currentUser()->hasPermission('administer users')) {
if ($this->currentUser->hasPermission('administer users')) {
// In addition, if the user is administrator, we need to make sure to
// match the anonymous user, that doesn't actually have a name in the
// database.
@ -128,7 +178,7 @@ class UserSelection extends SelectionBase {
// field, and concatenate the field and the condition separately.
$value_part = db_and();
$value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
$value_part->compile(Database::getConnection(), $query);
$value_part->compile($this->connection, $query);
$or->condition(db_and()
->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => user_format_name(user_load(0))))
->condition('base_table.uid', 0)
@ -138,4 +188,5 @@ class UserSelection extends SelectionBase {
}
}
}
}

View File

@ -2,17 +2,20 @@
/**
* @file
* Contains \Drupal\views\Plugin\entity_reference\selection\ViewsSelection.
* Contains \Drupal\views\Plugin\EntityReferenceSelection\ViewsSelection.
*/
namespace Drupal\views\Plugin\entity_reference\selection;
namespace Drupal\views\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityManagerInterface;
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\Url;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'selection' entity_reference.
@ -24,21 +27,14 @@ use Drupal\views\Views;
* weight = 0
* )
*/
class ViewsSelection implements SelectionInterface {
class ViewsSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
/**
* The field definition.
* The entity manager.
*
* @var \Drupal\Core\Field\FieldDefinitionInterface
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $fieldDefinition;
/**
* The entity object, or NULL
*
* @var \Drupal\Core\Entity\EntityInterface|null
*/
protected $entity;
protected $entityManager;
/**
* The loaded View object.
@ -48,23 +44,45 @@ class ViewsSelection implements SelectionInterface {
protected $view;
/**
* Constructs a View selection handler.
* Constructs a new ViewsSelection 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.
*/
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) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function settingsForm(FieldDefinitionInterface $field_definition) {
$selection_handler_settings = $field_definition->getSetting('handler_settings') ?: array();
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$selection_handler_settings = $this->configuration['handler_settings'];
$view_settings = !empty($selection_handler_settings['view']) ? $selection_handler_settings['view'] : array();
$displays = Views::getApplicableViews('entity_reference_display');
// Filter views that list the entity type we want, and group the separate
// displays by view.
$entity_type = \Drupal::entityManager()->getDefinition($field_definition->getSetting('target_type'));
$entity_type = $this->entityManager->getDefinition($this->configuration['target_type']);
$options = array();
foreach ($displays as $data) {
list($view, $display_id) = $data;
@ -79,40 +97,49 @@ class ViewsSelection implements SelectionInterface {
// into 'view_name' and 'view_display' in the final submitted values, so
// we massage the data at validate time on the wrapping element (not
// ideal).
$plugin = new static($field_definition);
$form['view']['#element_validate'] = array(array($plugin, 'settingsFormValidate'));
$form['view']['#element_validate'] = array(array(get_called_class(), 'settingsFormValidate'));
if ($options) {
$default = !empty($view_settings['view_name']) ? $view_settings['view_name'] . ':' . $view_settings['display_name'] : NULL;
$form['view']['view_and_display'] = array(
'#type' => 'select',
'#title' => t('View used to select the entities'),
'#title' => $this->t('View used to select the entities'),
'#required' => TRUE,
'#options' => $options,
'#default_value' => $default,
'#description' => '<p>' . t('Choose the view and display that select the entities that can be referenced.<br />Only views with a display of type "Entity Reference" are eligible.') . '</p>',
'#description' => '<p>' . $this->t('Choose the view and display that select the entities that can be referenced.<br />Only views with a display of type "Entity Reference" are eligible.') . '</p>',
);
$default = !empty($view_settings['arguments']) ? implode(', ', $view_settings['arguments']) : '';
$form['view']['arguments'] = array(
'#type' => 'textfield',
'#title' => t('View arguments'),
'#title' => $this->t('View arguments'),
'#default_value' => $default,
'#required' => FALSE,
'#description' => t('Provide a comma separated list of arguments to pass to the view.'),
'#description' => $this->t('Provide a comma separated list of arguments to pass to the view.'),
);
}
else {
$form['view']['no_view_help'] = array(
'#markup' => '<p>' . t('No eligible views were found. <a href="@create">Create a view</a> with an <em>Entity Reference</em> display, or add such a display to an <a href="@existing">existing view</a>.', array(
'@create' => \Drupal::url('views_ui.add'),
'@existing' => \Drupal::url('entity.view.collection'),
'#markup' => '<p>' . $this->t('No eligible views were found. <a href="@create">Create a view</a> with an <em>Entity Reference</em> display, or add such a display to an <a href="@existing">existing view</a>.', array(
'@create' => Url::fromRoute('views_ui.add'),
'@existing' => Url::fromRoute('entity.view.collection'),
)) . '</p>',
);
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* Initializes a view.
*
@ -131,14 +158,14 @@ class ViewsSelection implements SelectionInterface {
* Return TRUE if the view was initialized, FALSE otherwise.
*/
protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $limit = 0, $ids = NULL) {
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$handler_settings = $this->configuration['handler_settings'];
$view_name = $handler_settings['view']['view_name'];
$display_name = $handler_settings['view']['display_name'];
// Check that the view is valid and the display still exists.
$this->view = Views::getView($view_name);
if (!$this->view || !$this->view->access($display_name)) {
drupal_set_message(t('The reference view %view_name used in the %field_name field cannot be found.', array('%view_name' => $view_name, '%field_name' => $this->fieldDefinition->getLabel())), 'warning');
drupal_set_message(t('The reference view %view_name cannot be found.', array('%view_name' => $view_name)), 'warning');
return FALSE;
}
$this->view->setDisplay($display_name);
@ -158,7 +185,7 @@ class ViewsSelection implements SelectionInterface {
* {@inheritdoc}
*/
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$handler_settings = $this->configuration['handler_settings'];
$display_name = $handler_settings['view']['display_name'];
$arguments = $handler_settings['view']['arguments'];
$result = array();
@ -189,7 +216,7 @@ class ViewsSelection implements SelectionInterface {
* {@inheritdoc}
*/
public function validateReferenceableEntities(array $ids) {
$handler_settings = $this->fieldDefinition->getSetting('handler_settings');
$handler_settings = $this->configuration['handler_settings'];
$display_name = $handler_settings['view']['display_name'];
$arguments = $handler_settings['view']['arguments'];
$result = array();
@ -222,7 +249,7 @@ class ViewsSelection implements SelectionInterface {
list($view, $display) = explode(':', $element['view_and_display']['#value']);
}
else {
$form_state->setError($element, t('The views entity selection mode requires a view.'));
$form_state->setError($element, $this->t('The views entity selection mode requires a view.'));
return;
}