View source
<?php
namespace Drupal\entityreference_filter\Plugin\views\filter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\views\Entity\View;
use Drupal\views\Plugin\views\filter\ManyToOne;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class EntityReferenceFilterViewResult extends ManyToOne {
public $validatedExposedInput = NULL;
protected $loggerChannel;
protected $languageManager;
protected $viewStorage;
protected $entityRepository;
protected $pathCurrent;
protected $entityTypeManager;
public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger_channel, LanguageManagerInterface $language_manager, ConfigEntityStorageInterface $view_storage, EntityRepositoryInterface $entity_repository, CurrentPathStack $path_current, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->loggerChannel = $logger_channel;
$this->languageManager = $language_manager;
$this->viewStorage = $view_storage;
$this->entityRepository = $entity_repository;
$this->pathCurrent = $path_current;
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('logger.factory')
->get('entityreference_filter'), $container
->get('language_manager'), $container
->get('entity_type.manager')
->getStorage('view'), $container
->get('entity.repository'), $container
->get('path.current'), $container
->get('entity_type.manager'));
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['type'] = [
'default' => 'select',
];
$options['reference_display'] = [
'default' => '',
];
$options['reference_arguments'] = [
'default' => '',
];
$options['hide_empty_filter'] = [
'default' => TRUE,
];
return $options;
}
public function hasExtraOptions() {
return TRUE;
}
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
$form['type'] = [
'#type' => 'select',
'#title' => $this
->t('Selection type'),
'#options' => [
'select' => $this
->t('Dropdown'),
'textfield' => $this
->t('Autocomplete'),
],
'#default_value' => $this->options['type'],
'#disabled' => TRUE,
];
}
public function buildExposeForm(&$form, FormStateInterface $form_state) {
parent::buildExposeForm($form, $form_state);
$form['hide_empty_filter'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Hide empty filter'),
'#description' => $this
->t('Hide the exposed widget if the entity list is empty.'),
'#default_value' => $this->options['hide_empty_filter'],
];
$form['expose']['reduce']['#access'] = FALSE;
}
public function getValueOptions() {
if (isset($this->valueOptions)) {
return $this->valueOptions;
}
$this->valueOptions = $this
->getConfiguredViewsOptions();
return $this->valueOptions;
}
public function getConfiguredViewsOptions() {
$options = [];
if (empty($this->options['reference_display'])) {
return [];
}
[
$view_name,
$display_id,
] = explode(':', $this->options['reference_display']);
$view = Views::getView($view_name);
if (!$view || !$view
->access($display_id)) {
$this->loggerChannel
->warning('The view %view_name is no longer eligible for the filter.', [
'%view_name' => $view_name,
]);
return $options;
}
if ($view instanceof ViewExecutable) {
$args = $this
->getFilterArgs();
$view
->setDisplay($display_id);
$view
->setItemsPerPage(0);
$entity_reference_options = [
'limit' => NULL,
];
$view->displayHandlers
->get($display_id)
->setOption('entity_reference_options', $entity_reference_options);
$results = $view
->executeDisplay($display_id, $args);
foreach ($results as $renderable) {
$entity = $renderable["#row"]->_entity;
$render = $this
->getRenderer();
$option = $render
->renderPlain($renderable);
$options[$entity
->id()] = strip_tags($option);
}
}
return $options;
}
protected function getFilterArgs() {
$args = [];
$filters_by_identifier = [];
$view_args = $this
->getViewArgs();
$view_context_args = $this
->getViewContextArgs();
$reference_arguments = $this->options['reference_arguments'] ?? NULL;
if (isset($reference_arguments)) {
$arg_str = trim($reference_arguments);
if ($arg_str !== '') {
$args = explode('/', $arg_str);
foreach ($args as $i => $arg) {
$arg = trim($arg);
$first_char = mb_substr($arg, 0, 1);
if ($first_char === '!') {
$arg_no = (int) mb_substr($arg, 1) - 1;
if ($arg_no >= 0) {
$args[$i] = $view_args[$arg_no] ?? NULL;
}
}
if ($first_char === '[' && mb_substr($arg, -1) === ']') {
if (empty($filters_by_identifier) && !empty($this->view->filter)) {
foreach ($this->view->filter as $filter_handler) {
if (!$filter_handler
->isExposed()) {
continue;
}
$filters_by_identifier[$filter_handler->options['expose']['identifier']] = $filter_handler;
}
}
$args[$i] = NULL;
$filter_name = mb_substr($arg, 1, -1);
$input = $this->view
->getExposedInput();
if (isset($input[$filter_name])) {
$args[$i] = $input[$filter_name];
}
elseif (isset($filters_by_identifier[$filter_name])) {
$args[$i] = $filters_by_identifier[$filter_name]->value;
}
if (is_array($args[$i]) && !empty($args[$i])) {
$args[$i] = implode('+', $args[$i]);
}
}
if ($first_char === '#' && !empty($view_context_args)) {
$arg_no = (int) mb_substr($arg, 1) - 1;
$args[$i] = $view_context_args[$arg_no] ?? NULL;
}
if ($args[$i] === 'All' || $args[$i] === []) {
$args[$i] = NULL;
}
}
}
}
return $args;
}
protected function getViewArgs() {
return $this->view->args;
}
protected function getViewContextArgs() {
$args = [];
$arguments = $this->view->argument ?? [];
foreach ($arguments as $handler) {
$args[] = $handler
->getValue();
}
return $args;
}
protected function getControllingFilters() {
$filters = [];
if (isset($this->options['reference_arguments'])) {
$arg_str = trim($this->options['reference_arguments']);
if ($arg_str !== '') {
$args = explode('/', $arg_str);
foreach ($args as $arg) {
$arg = trim($arg);
$first_char = mb_substr($arg, 0, 1);
if ($first_char === '[' && mb_substr($arg, -1, 1) === ']') {
$filter_name = mb_substr($arg, 1, -1);
$filters[] = $filter_name;
}
}
}
}
return $filters;
}
public function afterBuild(array $element, FormStateInterface $form_state) {
$identifier = $this->options['expose']['identifier'];
$form_id = $element['#id'];
$controlling_filters = $this
->getControllingFilters();
foreach ($controlling_filters as $filter) {
if (isset($element[$filter])) {
if (!isset($element[$filter]['#attributes'])) {
$element[$filter]['#attributes'] = [];
}
$element[$filter]['#attributes']['autocomplete'] = 'off';
foreach (Element::children($element[$filter]) as $child) {
if (!isset($element[$filter][$child]['#attributes'])) {
$element[$filter][$child]['#attributes'] = [];
}
$element[$filter][$child]['#attributes']['autocomplete'] = 'off';
}
}
}
$exposed_plugin = $this->view->display_handler
->getPlugin('exposed_form');
$exposed_plugin_options = $exposed_plugin->options ?? NULL;
$autosubmit = $exposed_plugin_options['bef']['general']['autosubmit'] ?? FALSE;
if (!$autosubmit && !empty($controlling_filters)) {
$element['#attached']['drupalSettings']['entityreference_filter'][$form_id]['view'] = [
'view_name' => $this->view->storage
->id(),
'view_display_id' => $this->view->current_display,
'view_args' => Html::escape(implode('/', $this
->getViewArgs())),
'view_context_args' => $this
->getViewContextArgs(),
'view_path' => Html::escape($this->pathCurrent
->getPath()),
'view_base_path' => $this->view
->getPath(),
'view_dom_id' => $this->view->dom_id,
'ajax_path' => Url::fromRoute('entityreference_filter.ajax')
->toString(),
];
$element['#attached']['drupalSettings']['entityreference_filter'][$form_id]['dependent_filters_data'][$identifier] = $controlling_filters;
}
return $element;
}
protected function valueForm(&$form, FormStateInterface $form_state) {
$values = $this
->getValueOptions();
$exposed = $form_state
->get('exposed');
$filter_is_required = $this->options['expose']['required'];
$filter_is_multiple = $this->options['expose']['multiple'];
$filter_empty_hide = $this->options['hide_empty_filter'];
if ($this->options['type'] === 'textfield') {
}
else {
$default_value = (array) $this->value;
if ($exposed) {
$identifier = $this->options['expose']['identifier'];
$user_input = $form_state
->getUserInput();
if (isset($this->options['reference_arguments']) && strpos($this->options['reference_arguments'], '[') !== FALSE) {
$form['#attached'] = [
'library' => [
'entityreference_filter/entityreference_filter',
],
];
$form['#after_build'][] = [
$this,
'afterBuild',
];
}
$default_value = $user_input[$identifier] ?? [];
if (!is_array($default_value)) {
$default_value = [
$default_value,
];
}
$default_value = array_intersect($default_value, array_keys($values));
if (!$filter_is_multiple) {
if (!$filter_is_required && empty($default_value)) {
$default_value = 'All';
}
elseif (empty($default_value)) {
$keys = array_keys($values);
$default_value = array_shift($keys);
}
else {
$copy = $default_value;
$default_value = array_shift($copy);
}
}
}
$form['value'] = [
'#type' => 'select',
'#title' => $this
->t('Reference filter'),
'#multiple' => TRUE,
'#options' => $values,
'#size' => min(9, count($values)),
'#default_value' => $default_value,
];
if ($exposed && isset($identifier)) {
$user_input[$identifier] = $default_value;
$form_state
->setUserInput($user_input);
}
}
if (empty($values) && $exposed && $filter_empty_hide) {
$form['value']['#prefix'] = '<div class="hidden">';
$form['value']['#suffix'] = '</div>';
}
if (!$exposed) {
$options = [];
$views_ids = [];
$form['value']['#access'] = FALSE;
$filter_base_table = !empty($this->definition['filter_base_table']) ? $this->definition['filter_base_table'] : NULL;
if ($filter_base_table) {
$views_ids = $this->viewStorage
->getQuery()
->condition('status', TRUE)
->condition('display.*.display_plugin', 'entity_reference')
->condition('base_table', $filter_base_table)
->execute();
}
foreach ($this->viewStorage
->loadMultiple($views_ids) as $view) {
foreach ($view
->get('display') as $display_id => $display) {
$enabled = !empty($display['display_options']['enabled']) || !array_key_exists('enabled', $display['display_options']);
if ($enabled && $display['display_plugin'] === 'entity_reference') {
$options[$view
->id() . ':' . $display_id] = $view
->label() . ' - ' . $display['display_title'];
}
}
}
$show_reference_arguments_field = TRUE;
$description = '<p>' . $this
->t('Choose the view and display that select the entities that can be referenced. Only views with a display of type "Entity Reference" are eligible.') . '</p>';
if (empty($options)) {
$options = [
$this->options['reference_display'] => $this
->t('None'),
] + $options;
$warning = '<em>' . $this
->t('No views to use. At first, create a view display type "Entity Reference" with the same entity type as default filter values.') . '</em>';
$description = $warning;
$show_reference_arguments_field = FALSE;
}
$form['reference_display'] = [
'#type' => 'select',
'#title' => $this
->t('View used to select the entities'),
'#required' => TRUE,
'#options' => $options,
'#default_value' => $this->options['reference_display'],
'#description' => $description,
];
if (empty($this->options['reference_display'])) {
$form['reference_display']['#description'] .= '<p>' . $this
->t('Entity list will be available after saving this setting.') . '</p>';
}
$form['reference_arguments'] = [
'#type' => 'textfield',
'#size' => 50,
'#maxlength' => 256,
'#title' => $this
->t('Arguments for the view'),
'#default_value' => $this->options['reference_arguments'],
'#description' => $this
->t('Define arguments to send them to the selected entity reference view, they are received as contextual filter values in the same order.
Format is arg1/arg2/...argN starting from position 1. Possible arguments types are:') . '<br>' . $this
->t('!n - argument number n of the view dynamic URL argument %') . '<br>' . $this
->t('#n - argument number n of the contextual filter value') . '<br>' . $this
->t('[filter_identifier] - `Filter identifier` of the named exposed filter') . '<br>' . $this
->t('and other strings are passed as is.'),
'#access' => $show_reference_arguments_field,
];
$this->helper
->buildOptionsForm($form, $form_state);
}
}
protected function valueValidate($form, FormStateInterface $form_state) {
if ($this->options['type'] !== 'textfield') {
return;
}
}
protected function valueSubmit($form, FormStateInterface $form_state) {
$form_state
->setValue([
'options',
'value',
], NULL);
}
public function validateExposed(&$form, FormStateInterface $form_state) {
$exposed = $this
->isExposed();
$identifier = $this->options['expose']['identifier'];
if (!$exposed || !$identifier) {
return;
}
if ($this->options['type'] !== 'textfield') {
if ($form_state
->getValue($identifier) !== 'All') {
$this->validatedExposedInput = (array) $form_state
->getValue($identifier);
}
return;
}
}
public function acceptExposedInput($input) {
$exposed = $this
->isExposed();
$filter_is_required = $this->options['expose']['required'];
if (!$exposed) {
return TRUE;
}
if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
$this->operator = $input[$this->options['expose']['operator_id']];
}
if (!empty($this->view->is_attachment) && $this->view->display_handler
->usesExposed()) {
$this->validatedExposedInput = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
}
if ($this->operator === 'empty' || $this->operator === 'not empty') {
return TRUE;
}
if (!$filter_is_required && empty($this->validatedExposedInput)) {
return FALSE;
}
$rewrite_query = parent::acceptExposedInput($input);
if ($rewrite_query && isset($this->validatedExposedInput)) {
$this->value = $this->validatedExposedInput;
}
return $rewrite_query;
}
public function query() {
$exposed = $this
->isExposed();
if (!$exposed) {
$options = $this
->getValueOptions();
$values = !empty($options) ? array_keys($options) : [
'0',
];
$this->value = $values;
}
parent::query();
}
public function isExposed() {
return !empty($this->options['exposed']) ? TRUE : FALSE;
}
public function adminSummary() {
$this
->getValueOptions();
return parent::adminSummary();
}
public function getCacheContexts() {
$contexts = parent::getCacheContexts();
$contexts[] = 'user';
return $contexts;
}
public function getCacheTags() {
$reference_display = $this->options['reference_display'] ?? FALSE;
if ($reference_display) {
[
$view_name,
] = explode(':', $reference_display);
$config = $this->viewStorage
->load($view_name);
if ($config && $config instanceof View) {
$definitions = $this->entityTypeManager
->getDefinitions();
foreach ($definitions as $definition) {
$base_table_view = $config
->get('base_table');
if ($definition instanceof ContentEntityTypeInterface) {
$base_table_entity = $definition
->getDataTable();
if (!$base_table_entity) {
$base_table_entity = $definition
->getBaseTable();
}
if ($base_table_entity === $base_table_view) {
return $definition
->getListCacheTags();
}
}
}
}
}
return parent::getCacheTags();
}
public function calculateDependencies() {
$dependencies = parent::calculateDependencies();
$reference_display = $this->options['reference_display'] ?? FALSE;
if ($reference_display) {
[
$view_name,
] = explode(':', $reference_display);
$config = $this->viewStorage
->load($view_name);
if (empty($config) || !$config instanceof View) {
return $dependencies;
}
$dependencies[$config
->getConfigDependencyKey()][] = $config
->getConfigDependencyName();
}
return $dependencies;
}
}