View source  
  <?php
namespace Drupal\Core\Entity\Plugin\EntityReferenceSelection;
use Drupal\Component\Utility\Html;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DefaultSelection extends PluginBase implements SelectionInterface, SelectionWithAutocreateInterface, ContainerFactoryPluginInterface {
  
  protected $entityManager;
  
  protected $moduleHandler;
  
  protected $currentUser;
  
  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;
  }
  
  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'));
  }
  
  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);
    
    $selection_handler_settings += array(
      
      'target_bundles' => NULL,
      'sort' => array(
        'field' => '_none',
      ),
      'auto_create' => FALSE,
    );
    if ($entity_type
      ->hasKey('bundle')) {
      $bundle_options = array();
      foreach ($bundles as $bundle_name => $bundle_info) {
        $bundle_options[$bundle_name] = $bundle_info['label'];
      }
      $form['target_bundles'] = array(
        '#type' => 'checkboxes',
        '#title' => $this
          ->t('Bundles'),
        '#options' => $bundle_options,
        '#default_value' => (array) $selection_handler_settings['target_bundles'],
        '#required' => TRUE,
        '#size' => 6,
        '#multiple' => TRUE,
        '#element_validate' => [
          [
            get_class($this),
            'elementValidateFilter',
          ],
        ],
      );
    }
    else {
      $form['target_bundles'] = array(
        '#type' => 'value',
        '#value' => array(),
      );
    }
    if ($entity_type
      ->isSubclassOf('\\Drupal\\Core\\Entity\\FieldableEntityInterface')) {
      $fields = array();
      foreach (array_keys($bundles) as $bundle) {
        $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) {
          
          $columns = $field_definition
            ->getFieldStorageDefinition()
            ->getColumns();
          
          if (count($columns) > 1) {
            foreach ($columns as $column_name => $column_info) {
              $fields[$field_name . '.' . $column_name] = $this
                ->t('@label (@column)', array(
                '@label' => $field_definition
                  ->getLabel(),
                '@column' => $column_name,
              ));
            }
          }
          else {
            $fields[$field_name] = $this
              ->t('@label', array(
              '@label' => $field_definition
                ->getLabel(),
            ));
          }
        }
      }
      $form['sort']['field'] = array(
        '#type' => 'select',
        '#title' => $this
          ->t('Sort by'),
        '#options' => array(
          '_none' => $this
            ->t('- None -'),
        ) + $fields,
        '#ajax' => TRUE,
        '#limit_validation_errors' => array(),
        '#default_value' => $selection_handler_settings['sort']['field'],
      );
      $form['sort']['settings'] = array(
        '#type' => 'container',
        '#attributes' => array(
          'class' => array(
            'entity_reference-settings',
          ),
        ),
        '#process' => [
          [
            EntityReferenceItem::class,
            'formProcessMergeParent',
          ],
        ],
      );
      if ($selection_handler_settings['sort']['field'] != '_none') {
        
        $selection_handler_settings['sort'] += array(
          'direction' => 'ASC',
        );
        $form['sort']['settings']['direction'] = array(
          '#type' => 'select',
          '#title' => $this
            ->t('Sort direction'),
          '#required' => TRUE,
          '#options' => array(
            'ASC' => $this
              ->t('Ascending'),
            'DESC' => $this
              ->t('Descending'),
          ),
          '#default_value' => $selection_handler_settings['sort']['direction'],
        );
      }
    }
    return $form;
  }
  
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    
    if ($form_state
      ->getValue([
      'settings',
      'handler_settings',
      'target_bundles',
    ]) === []) {
      $form_state
        ->setValue([
        'settings',
        'handler_settings',
        'target_bundles',
      ], NULL);
    }
  }
  
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  }
  
  public static function elementValidateFilter(&$element, FormStateInterface $form_state) {
    $element['#value'] = array_filter($element['#value']);
    $form_state
      ->setValueForElement($element, $element['#value']);
  }
  
  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
    $target_type = $this->configuration['target_type'];
    $query = $this
      ->buildEntityQuery($match, $match_operator);
    if ($limit > 0) {
      $query
        ->range(0, $limit);
    }
    $result = $query
      ->execute();
    if (empty($result)) {
      return array();
    }
    $options = array();
    $entities = entity_load_multiple($target_type, $result);
    foreach ($entities as $entity_id => $entity) {
      $bundle = $entity
        ->bundle();
      $options[$bundle][$entity_id] = Html::escape($entity
        ->label());
    }
    return $options;
  }
  
  public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') {
    $query = $this
      ->buildEntityQuery($match, $match_operator);
    return $query
      ->count()
      ->execute();
  }
  
  public function validateReferenceableEntities(array $ids) {
    $result = array();
    if ($ids) {
      $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')
        ->execute();
    }
    return $result;
  }
  
  public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
    $entity_type = $this->entityManager
      ->getDefinition($entity_type_id);
    $bundle_key = $entity_type
      ->getKey('bundle');
    $label_key = $entity_type
      ->getKey('label');
    $entity = $this->entityManager
      ->getStorage($entity_type_id)
      ->create(array(
      $bundle_key => $bundle,
      $label_key => $label,
    ));
    if ($entity instanceof EntityOwnerInterface) {
      $entity
        ->setOwnerId($uid);
    }
    return $entity;
  }
  
  public function validateReferenceableNewEntities(array $entities) {
    return array_filter($entities, function ($entity) {
      if (isset($this->configuration['handler_settings']['target_bundles'])) {
        return in_array($entity
          ->bundle(), $this->configuration['handler_settings']['target_bundles']);
      }
      return TRUE;
    });
  }
  
  protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
    $target_type = $this->configuration['target_type'];
    $handler_settings = $this->configuration['handler_settings'];
    $entity_type = $this->entityManager
      ->getDefinition($target_type);
    $query = $this->entityManager
      ->getStorage($target_type)
      ->getQuery();
    
    if (isset($handler_settings['target_bundles']) && is_array($handler_settings['target_bundles'])) {
      
      if ($handler_settings['target_bundles'] === []) {
        $query
          ->condition($entity_type
          ->getKey('id'), NULL, '=');
        return $query;
      }
      else {
        $query
          ->condition($entity_type
          ->getKey('bundle'), $handler_settings['target_bundles'], 'IN');
      }
    }
    if (isset($match) && ($label_key = $entity_type
      ->getKey('label'))) {
      $query
        ->condition($label_key, $match, $match_operator);
    }
    
    $query
      ->addTag($target_type . '_access');
    
    $query
      ->addTag('entity_reference');
    $query
      ->addMetaData('entity_reference_selection_handler', $this);
    
    if (!empty($handler_settings['sort'])) {
      $sort_settings = $handler_settings['sort'];
      if ($sort_settings['field'] != '_none') {
        $query
          ->sort($sort_settings['field'], $sort_settings['direction']);
      }
    }
    return $query;
  }
  
  public function entityQueryAlter(SelectInterface $query) {
  }
  
  protected function reAlterQuery(AlterableInterface $query, $tag, $base_table) {
    
    $old_tags = $query->alterTags;
    $old_metadata = $query->alterMetaData;
    $query->alterTags = array(
      $tag => TRUE,
    );
    $query->alterMetaData['base_table'] = $base_table;
    $this->moduleHandler
      ->alter(array(
      'query',
      'query_' . $tag,
    ), $query);
    
    $query->alterTags = $old_tags;
    $query->alterMetaData = $old_metadata;
  }
}