You are here

class DefaultSelection in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php \Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection

Default plugin implementation of the Entity Reference Selection plugin.

Also serves as a base class for specific types of Entity Reference Selection plugins.

Plugin annotation


@EntityReferenceSelection(
  id = "default",
  label = @Translation("Default"),
  group = "default",
  weight = 0,
  deriver = "Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver"
)

Hierarchy

Expanded class hierarchy of DefaultSelection

See also

\Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager

\Drupal\Core\Entity\Annotation\EntityReferenceSelection

\Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface

\Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver

Plugin API

8 files declare their use of DefaultSelection
CommentSelection.php in core/modules/comment/src/Plugin/EntityReferenceSelection/CommentSelection.php
FileSelection.php in core/modules/file/src/Plugin/EntityReferenceSelection/FileSelection.php
MediaSelection.php in core/modules/media/src/Plugin/EntityReferenceSelection/MediaSelection.php
NodeSelection.php in core/modules/node/src/Plugin/EntityReferenceSelection/NodeSelection.php
TermSelection.php in core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php

... See full list

File

core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/DefaultSelection.php, line 42

Namespace

Drupal\Core\Entity\Plugin\EntityReferenceSelection
View source
class DefaultSelection extends SelectionPluginBase implements ContainerFactoryPluginInterface, SelectionWithAutocreateInterface {

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The entity field manager service.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * Entity type bundle info service.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  public $entityTypeBundleInfo;

  /**
   * The entity repository.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * Constructs a new DefaultSelection 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\EntityTypeManagerInterface $entity_type_manager
   *   The entity type 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\Entity\EntityFieldManagerInterface $entity_field_manager
   *   The entity field manager.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
   *   The entity type bundle info service.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user, EntityFieldManagerInterface $entity_field_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, EntityRepositoryInterface $entity_repository) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeManager = $entity_type_manager;
    $this->moduleHandler = $module_handler;
    $this->currentUser = $current_user;
    $this->entityFieldManager = $entity_field_manager;
    $this->entityTypeBundleInfo = $entity_type_bundle_info;
    $this->entityRepository = $entity_repository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container
      ->get('entity_type.manager'), $container
      ->get('module_handler'), $container
      ->get('current_user'), $container
      ->get('entity_field.manager'), $container
      ->get('entity_type.bundle.info'), $container
      ->get('entity.repository'));
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      // For the 'target_bundles' setting, a NULL value is equivalent to "allow
      // entities from any bundle to be referenced" and an empty array value is
      // equivalent to "no entities from any bundle can be referenced".
      'target_bundles' => NULL,
      'sort' => [
        'field' => '_none',
        'direction' => 'ASC',
      ],
      'auto_create' => FALSE,
      'auto_create_bundle' => NULL,
    ] + parent::defaultConfiguration();
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $configuration = $this
      ->getConfiguration();
    $entity_type_id = $configuration['target_type'];
    $entity_type = $this->entityTypeManager
      ->getDefinition($entity_type_id);
    $bundles = $this->entityTypeBundleInfo
      ->getBundleInfo($entity_type_id);
    if ($entity_type
      ->hasKey('bundle')) {
      $bundle_options = [];
      foreach ($bundles as $bundle_name => $bundle_info) {
        $bundle_options[$bundle_name] = $bundle_info['label'];
      }
      natsort($bundle_options);
      $selected_bundles = array_intersect_key($bundle_options, array_filter((array) $configuration['target_bundles']));
      $form['target_bundles'] = [
        '#type' => 'checkboxes',
        '#title' => $entity_type
          ->getBundleLabel(),
        '#options' => $bundle_options,
        '#default_value' => (array) $configuration['target_bundles'],
        '#required' => TRUE,
        '#size' => 6,
        '#multiple' => TRUE,
        '#element_validate' => [
          [
            static::class,
            'elementValidateFilter',
          ],
        ],
        '#ajax' => TRUE,
        '#limit_validation_errors' => [],
      ];
      $form['target_bundles_update'] = [
        '#type' => 'submit',
        '#value' => $this
          ->t('Update form'),
        '#limit_validation_errors' => [],
        '#attributes' => [
          'class' => [
            'js-hide',
          ],
        ],
        '#submit' => [
          [
            EntityReferenceItem::class,
            'settingsAjaxSubmit',
          ],
        ],
      ];
    }
    else {
      $form['target_bundles'] = [
        '#type' => 'value',
        '#value' => [],
      ];
    }
    if ($entity_type
      ->entityClassImplements(FieldableEntityInterface::class)) {
      $options = $entity_type
        ->hasKey('bundle') ? $selected_bundles : $bundles;
      $fields = [];
      foreach (array_keys($options) as $bundle) {
        $bundle_fields = array_filter($this->entityFieldManager
          ->getFieldDefinitions($entity_type_id, $bundle), function ($field_definition) {
          return !$field_definition
            ->isComputed();
        });
        foreach ($bundle_fields as $field_name => $field_definition) {

          /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
          $columns = $field_definition
            ->getFieldStorageDefinition()
            ->getColumns();

          // If there is more than one column, display them all, otherwise just
          // display the field label.
          // @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] = $this
                ->t('@label (@column)', [
                '@label' => $field_definition
                  ->getLabel(),
                '@column' => $column_name,
              ]);
            }
          }
          else {
            $fields[$field_name] = $this
              ->t('@label', [
              '@label' => $field_definition
                ->getLabel(),
            ]);
          }
        }
      }
      $form['sort']['field'] = [
        '#type' => 'select',
        '#title' => $this
          ->t('Sort by'),
        '#options' => $fields,
        '#ajax' => TRUE,
        '#empty_value' => '_none',
        '#sort_options' => TRUE,
        '#limit_validation_errors' => [],
        '#default_value' => $configuration['sort']['field'],
      ];
      if ($entity_type
        ->hasKey('bundle')) {
        $form['sort']['field']['#states'] = [
          'visible' => [
            ':input[name^="settings[handler_settings][target_bundles]["]' => [
              'checked' => TRUE,
            ],
          ],
        ];
      }
      $form['sort']['settings'] = [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'entity_reference-settings',
          ],
        ],
        '#process' => [
          [
            EntityReferenceItem::class,
            'formProcessMergeParent',
          ],
        ],
      ];
      $form['sort']['settings']['direction'] = [
        '#type' => 'select',
        '#title' => $this
          ->t('Sort direction'),
        '#required' => TRUE,
        '#options' => [
          'ASC' => $this
            ->t('Ascending'),
          'DESC' => $this
            ->t('Descending'),
        ],
        '#default_value' => $configuration['sort']['direction'],
        '#states' => [
          'visible' => [
            ':input[name="settings[handler_settings][sort][field]"]' => [
              '!value' => '_none',
            ],
          ],
        ],
      ];
      if ($entity_type
        ->hasKey('bundle')) {
        $form['sort']['settings']['direction']['#states']['visible'][] = [
          ':input[name^="settings[handler_settings][target_bundles]["]' => [
            'checked' => TRUE,
          ],
        ];
      }
    }
    $form['auto_create'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t("Create referenced entities if they don't already exist"),
      '#default_value' => $configuration['auto_create'],
      '#weight' => -2,
    ];
    if ($entity_type
      ->hasKey('bundle')) {
      $form['auto_create_bundle'] = [
        '#type' => 'select',
        '#title' => $this
          ->t('Store new items in'),
        '#options' => $selected_bundles,
        '#default_value' => $configuration['auto_create_bundle'],
        '#access' => count($selected_bundles) > 1,
        '#states' => [
          'visible' => [
            ':input[name="settings[handler_settings][auto_create]"]' => [
              'checked' => TRUE,
            ],
          ],
        ],
        '#weight' => -1,
      ];
    }
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::validateConfigurationForm($form, $form_state);

    // If no checkboxes were checked for 'target_bundles', store NULL ("all
    // bundles are referenceable") rather than empty array ("no bundle is
    // referenceable" - typically happens when all referenceable bundles have
    // been deleted).
    if ($form_state
      ->getValue([
      'settings',
      'handler_settings',
      'target_bundles',
    ]) === []) {
      $form_state
        ->setValue([
        'settings',
        'handler_settings',
        'target_bundles',
      ], NULL);
    }

    // Don't store the 'target_bundles_update' button value into the field
    // config settings.
    $form_state
      ->unsetValue([
      'settings',
      'handler_settings',
      'target_bundles_update',
    ]);
  }

  /**
   * Form element validation handler; Filters the #value property of an element.
   */
  public static function elementValidateFilter(&$element, FormStateInterface $form_state) {
    $element['#value'] = array_filter($element['#value']);
    $form_state
      ->setValueForElement($element, $element['#value']);
  }

  /**
   * {@inheritdoc}
   */
  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
    $target_type = $this
      ->getConfiguration()['target_type'];
    $query = $this
      ->buildEntityQuery($match, $match_operator);
    if ($limit > 0) {
      $query
        ->range(0, $limit);
    }
    $result = $query
      ->execute();
    if (empty($result)) {
      return [];
    }
    $options = [];
    $entities = $this->entityTypeManager
      ->getStorage($target_type)
      ->loadMultiple($result);
    foreach ($entities as $entity_id => $entity) {
      $bundle = $entity
        ->bundle();
      $options[$bundle][$entity_id] = Html::escape($this->entityRepository
        ->getTranslationFromContext($entity)
        ->label());
    }
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') {
    $query = $this
      ->buildEntityQuery($match, $match_operator);
    return $query
      ->count()
      ->execute();
  }

  /**
   * {@inheritdoc}
   */
  public function validateReferenceableEntities(array $ids) {
    $result = [];
    if ($ids) {
      $target_type = $this->configuration['target_type'];
      $entity_type = $this->entityTypeManager
        ->getDefinition($target_type);
      $query = $this
        ->buildEntityQuery();
      $result = $query
        ->condition($entity_type
        ->getKey('id'), $ids, 'IN')
        ->execute();
    }
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
    $entity_type = $this->entityTypeManager
      ->getDefinition($entity_type_id);
    $values = [
      $entity_type
        ->getKey('label') => $label,
    ];
    if ($bundle_key = $entity_type
      ->getKey('bundle')) {
      $values[$bundle_key] = $bundle;
    }
    $entity = $this->entityTypeManager
      ->getStorage($entity_type_id)
      ->create($values);
    if ($entity instanceof EntityOwnerInterface) {
      $entity
        ->setOwnerId($uid);
    }
    return $entity;
  }

  /**
   * {@inheritdoc}
   */
  public function validateReferenceableNewEntities(array $entities) {
    return array_filter($entities, function ($entity) {
      $target_bundles = $this
        ->getConfiguration()['target_bundles'];
      if (isset($target_bundles)) {
        return in_array($entity
          ->bundle(), $target_bundles);
      }
      return TRUE;
    });
  }

  /**
   * Builds an EntityQuery to get referenceable entities.
   *
   * @param string|null $match
   *   (Optional) Text to match the label against. Defaults to NULL.
   * @param string $match_operator
   *   (Optional) The operation the matching should be done with. Defaults
   *   to "CONTAINS".
   *
   * @return \Drupal\Core\Entity\Query\QueryInterface
   *   The EntityQuery object with the basic conditions and sorting applied to
   *   it.
   */
  protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
    $configuration = $this
      ->getConfiguration();
    $target_type = $configuration['target_type'];
    $entity_type = $this->entityTypeManager
      ->getDefinition($target_type);
    $query = $this->entityTypeManager
      ->getStorage($target_type)
      ->getQuery();
    $query
      ->accessCheck(TRUE);

    // If 'target_bundles' is NULL, all bundles are referenceable, no further
    // conditions are needed.
    if (is_array($configuration['target_bundles'])) {

      // If 'target_bundles' is an empty array, no bundle is referenceable,
      // force the query to never return anything and bail out early.
      if ($configuration['target_bundles'] === []) {
        $query
          ->condition($entity_type
          ->getKey('id'), NULL, '=');
        return $query;
      }
      else {
        $query
          ->condition($entity_type
          ->getKey('bundle'), $configuration['target_bundles'], 'IN');
      }
    }
    if (isset($match) && ($label_key = $entity_type
      ->getKey('label'))) {
      $query
        ->condition($label_key, $match, $match_operator);
    }

    // Add entity-access tag.
    $query
      ->addTag($target_type . '_access');

    // Add the Selection handler for system_query_entity_reference_alter().
    $query
      ->addTag('entity_reference');
    $query
      ->addMetaData('entity_reference_selection_handler', $this);

    // Add the sort option.
    if ($configuration['sort']['field'] !== '_none') {
      $query
        ->sort($configuration['sort']['field'], $configuration['sort']['direction']);
    }
    return $query;
  }

  /**
   * Helper method: Passes a query to the alteration system again.
   *
   * This allows Entity Reference to add a tag to an existing query so it can
   * ask access control mechanisms to alter it again.
   */
  protected function reAlterQuery(AlterableInterface $query, $tag, $base_table) {

    // Save the old tags and metadata.
    // For some reason, those are public.
    $old_tags = $query->alterTags;
    $old_metadata = $query->alterMetaData;
    $query->alterTags = [
      $tag => TRUE,
    ];
    $query->alterMetaData['base_table'] = $base_table;
    $this->moduleHandler
      ->alter([
      'query',
      'query_' . $tag,
    ], $query);

    // Restore the tags and metadata.
    $query->alterTags = $old_tags;
    $query->alterMetaData = $old_metadata;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DefaultSelection::$currentUser protected property The current user.
DefaultSelection::$entityFieldManager protected property The entity field manager service.
DefaultSelection::$entityRepository protected property The entity repository.
DefaultSelection::$entityTypeBundleInfo public property Entity type bundle info service.
DefaultSelection::$entityTypeManager protected property The entity type manager service.
DefaultSelection::$moduleHandler protected property The module handler service.
DefaultSelection::buildConfigurationForm public function Form constructor. Overrides SelectionPluginBase::buildConfigurationForm 3
DefaultSelection::buildEntityQuery protected function Builds an EntityQuery to get referenceable entities. 7
DefaultSelection::countReferenceableEntities public function Counts entities that are referenceable. Overrides SelectionInterface::countReferenceableEntities 2
DefaultSelection::create public static function Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface::create 2
DefaultSelection::createNewEntity public function Creates a new entity object that can be used as a valid reference. Overrides SelectionWithAutocreateInterface::createNewEntity 6
DefaultSelection::defaultConfiguration public function Gets default configuration for this plugin. Overrides SelectionPluginBase::defaultConfiguration 3
DefaultSelection::elementValidateFilter public static function Form element validation handler; Filters the #value property of an element.
DefaultSelection::getReferenceableEntities public function Gets the list of referenceable entities. Overrides SelectionInterface::getReferenceableEntities 3
DefaultSelection::reAlterQuery protected function Helper method: Passes a query to the alteration system again.
DefaultSelection::validateConfigurationForm public function Form validation handler. Overrides SelectionPluginBase::validateConfigurationForm
DefaultSelection::validateReferenceableEntities public function Validates which existing entities can be referenced. Overrides SelectionInterface::validateReferenceableEntities
DefaultSelection::validateReferenceableNewEntities public function Validates which newly created entities can be referenced. Overrides SelectionWithAutocreateInterface::validateReferenceableNewEntities 6
DefaultSelection::__construct public function Constructs a new DefaultSelection object. Overrides SelectionPluginBase::__construct 1
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
MessengerTrait::$messenger protected property The messenger. 27
MessengerTrait::messenger public function Gets the messenger. 27
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 2
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
SelectionPluginBase::calculateDependencies public function Calculates dependencies for the configured plugin. Overrides DependentPluginInterface::calculateDependencies
SelectionPluginBase::entityQueryAlter public function Allows the selection to alter the SelectQuery generated by EntityFieldQuery. Overrides SelectionInterface::entityQueryAlter 2
SelectionPluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
SelectionPluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration
SelectionPluginBase::submitConfigurationForm public function Form submission handler. Overrides PluginFormInterface::submitConfigurationForm
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.