View source
<?php
namespace Drupal\views_selective_filters\Plugin\views\filter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\filter\InOperator;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
class Selective extends InOperator {
protected $originalOptions;
protected static $results;
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->options['exposed'] = TRUE;
$this->realField = $this->options['selective_display_field'];
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['selective_display_field']['default'] = '';
$options['selective_display_sort']['default'] = 'ASC';
$options['selective_aggregated_fields']['default'] = '';
$options['selective_items_limit']['default'] = 100;
return $options;
}
public function getValueOptions() {
if (isset($this->valueOptions)) {
return $this->valueOptions;
}
$this->valueOptions = [];
if (empty($this->view->selective_oids) && !empty($this->view->inited)) {
$this->valueOptions = $this
->getOids();
$this->valueOptions = array_diff_key($this->valueOptions, [
'' => NULL,
]);
$this->view->using_selective = TRUE;
}
else {
if (!empty($this->view->selective_oids)) {
$this->valueOptions = [];
}
else {
}
}
return $this->valueOptions;
}
protected function valueForm(&$form, FormStateInterface $form_state) {
$this
->getValueOptions();
if (isset($this->valueOptions) && is_array($this->valueOptions)) {
parent::valueForm($form, $form_state);
}
$form['value']['#validated'] = TRUE;
}
protected function baseFieldCompatible($base_field1, $base_field2) {
return strpos($base_field2, $base_field1) === 0;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
$base_field = $this->definition['field_base'];
parent::buildOptionsForm($form, $form_state);
array_unshift($form['expose_button'], [
'warning' => [
'#theme' => 'status_messages',
'#message_list' => [
'warning' => [
t('This filter is always exposed to users.'),
],
],
'#status_headings' => [
'status' => t('Status message'),
'error' => t('Error message'),
'warning' => t('Warning message'),
],
],
]);
$form['expose_button']['checkbox']['checkbox']['#type'] = 'hidden';
unset($form['expose_button']['button']);
unset($form['expose_button']['markup']);
$form['value']['#access'] = FALSE;
unset($form['group_button']);
$options = [];
foreach ($this->view->display_handler
->getHandlers('field') as $field) {
if ($this
->baseFieldCompatible($base_field, $field->field)) {
$options[$field->options['id']] = $field
->adminLabel();
}
}
$form['selective_display_field'] = [
'#title' => t('Display field'),
'#type' => 'select',
'#description' => t('Field to be used for the selective options.'),
'#options' => $options,
'#default_value' => $this->options['selective_display_field'],
];
$options = [];
$options['NONE'] = t('No sorting');
if ($this
->getOriginalOptions()) {
$options['ORIG'] = t('As the original filter');
}
$options['KASC'] = t('Custom key ascending (ksort)');
$options['KDESC'] = t('Custom key descending (ksort reverse)');
$options['ASC'] = t('Custom ascending (asort)');
$options['DESC'] = t('Custom descending (asort reverse)');
$form['selective_display_sort'] = [
'#title' => t('Sort field'),
'#type' => 'select',
'#description' => t('Choose wich field to use for display'),
'#options' => $options,
'#default_value' => $this->options['selective_display_sort'],
];
$form['selective_items_limit'] = [
'#title' => t('Limit number of select items'),
'#type' => 'textfield',
'#description' => t("Don't allow a badly configured selective filter to return thousands of possible values. Enter a limit or remove any value for no limit. We recommend to set a limit no higher than 100."),
'#default_value' => $this->options['selective_items_limit'],
'#min' => 0,
];
}
public function buildExposeForm(&$form, FormStateInterface $form_state) {
parent::buildExposeForm($form, $form_state);
unset($form['expose']['reduce']);
if (empty($form['expose']['identifier']['#default_value'])) {
$form['expose']['identifier']['#default_value'] = $this->options['field'];
}
if (empty($form['expose']['label']['#default_value'])) {
$form['expose']['label']['#default_value'] = $this->definition['title'];
}
if (empty($form['ui_name']['#default_value'])) {
$form['ui_name']['#default_value'] = $this->definition['title'];
}
}
public function query() {
if (isset($this->view->selective_handler_signature) && $this
->getSignature() === $this->view->selective_handler_signature) {
return;
}
parent::query();
}
protected function getSignature() {
return hash('sha256', serialize([
'id' => $this->view
->id(),
'args' => $this->view->args,
'input' => $this->view
->getExposedInput(),
'base_field' => $this->definition['field_base'],
'real_field' => $this->realField,
'field' => $this->field,
'table' => $this->table,
'ui_name' => $this
->adminLabel(),
]));
}
protected function getOids() {
$base_field = $this->definition['field_base'];
$ui_name = $this
->adminLabel();
$signature = $this
->getSignature();
if (empty(static::$results[$signature])) {
$max_items = (int) $this->options['selective_items_limit'];
$view_copy = Views::executableFactory()
->get($this->view->storage);
if (!$view_copy) {
return NULL;
}
$view_copy->selective_oids = TRUE;
$view_copy->selective_handler_signature = $signature;
$view_copy
->setExposedInput($this->view
->getExposedInput());
$view_copy
->setArguments($this->view->args);
$view_copy
->setItemsPerPage($max_items);
$view_copy
->setDisplay($this->view->current_display);
$display = $view_copy
->getDisplay();
unset($display->display['display_options']['exposed_form']);
$fields =& $display
->getHandlers('field');
$fields = array_intersect_key($fields, [
$this->options['selective_display_field'] => TRUE,
]);
if (empty($fields)) {
drupal_set_message(t('Selective query filter must have corresponding field added to view with Administrative Name set to "@name" and Base Type "@type"', [
'@name' => $ui_name,
'@type' => $base_field,
]), 'error');
return [];
}
$field = reset($fields);
$field_options = $field->options;
$field_id = $field_options['id'];
$no_display_field_relationship = empty($field_options['relationship']) || $field_options['relationship'] === 'none';
$no_filter_relationship = empty($this->options['relationship']) || $this->options['relationship'] === 'none';
$equal = $no_display_field_relationship === TRUE && $no_filter_relationship === TRUE || $field_options['relationship'] === $this->options['relationship'];
if (!$equal) {
drupal_set_message(t('Selective filter "@name": relationship of field and filter must match.', [
'@name' => $ui_name,
'@type' => $base_field,
]), 'error');
return [];
}
$field_options['exclude'] = 0;
$field_options['group_type'] = 'group';
$display
->setOption('group_by', 1);
$query_options = $display
->getOption('query');
$query_options['options']['distinct'] = TRUE;
$display
->setOption('query', $query_options);
$display
->setOption('pager', [
'type' => 'none',
'options' => [],
]);
$display
->setOption('style', [
'type' => 'default',
'options' => [],
]);
$display
->setOption('row', [
'type' => 'fields',
'options' => [],
]);
$display
->setOption('cache', [
'type' => 'none',
'options' => [],
]);
$view_copy
->execute($this->view->current_display);
if (method_exists($field, 'getValueOptions')) {
$field
->getValueOptions();
}
$style = $display
->getPlugin('style');
$oids = [];
foreach ($view_copy->result as $row) {
$key = $field
->getValue($row);
$key = is_array($key) ? reset($key) : $key;
$value = $style
->getField($row->index, $field_id);
$oids[$key] = SafeMarkup::checkPlain($value);
}
$sort_option = $this->options['selective_display_sort'];
switch ($sort_option) {
case 'ASC':
asort($oids);
break;
case 'DESC':
arsort($oids);
break;
case 'KASC':
ksort($oids);
break;
case 'KDESC':
krsort($oids);
break;
case 'ORIG':
$oids = static::filterOriginalOptions($this
->getOriginalOptions(), array_keys($oids));
break;
case 'NONE':
break;
default:
asort($oids);
}
if (!empty($max_items) && count($oids) == $max_items) {
drupal_set_message(t('Selective filter "@field" has limited the amount of total results. Please, review you query configuration.', [
'@field' => $ui_name,
]), 'warning');
}
static::$results[$signature] = $oids;
$view_copy
->destroy();
}
return static::$results[$signature];
}
protected static function filterOriginalOptions(array $options, array $set) {
$filtered = [];
foreach ($options as $key => $value) {
if (is_array($value)) {
$nested = static::filterOriginalOptions($value, $set);
if (!empty($nested)) {
$filtered[$key] = $nested;
}
continue;
}
if (in_array($key, $set)) {
$filtered[$key] = $value;
}
}
return $filtered;
}
protected function getOriginalOptions() {
if (!isset($this->originalOptions)) {
}
return $this->originalOptions;
}
}