1. 8.2.x core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
  2. 8.2.x core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
  3. 8.0.x core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
  4. 8.0.x core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
  5. 8.1.x core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php
  6. 8.1.x core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
  7. 8.3.x core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
  8. 8.3.x core/modules/taxonomy/src/Plugin/views/field/TaxonomyIndexTid.php

Namespace

Drupal\taxonomy\Plugin\views\filter

File

core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
View source
  1. <?php
  2. namespace Drupal\taxonomy\Plugin\views\filter;
  3. use Drupal\Core\Entity\Element\EntityAutocomplete;
  4. use Drupal\Core\Form\FormStateInterface;
  5. use Drupal\taxonomy\Entity\Term;
  6. use Drupal\taxonomy\TermStorageInterface;
  7. use Drupal\taxonomy\VocabularyStorageInterface;
  8. use Drupal\views\ViewExecutable;
  9. use Drupal\views\Plugin\views\display\DisplayPluginBase;
  10. use Drupal\views\Plugin\views\filter\ManyToOne;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. /**
  13. * Filter by term id.
  14. *
  15. * @ingroup views_filter_handlers
  16. *
  17. * @ViewsFilter("taxonomy_index_tid")
  18. */
  19. class TaxonomyIndexTid extends ManyToOne {
  20. // Stores the exposed input for this filter.
  21. public $validated_exposed_input = NULL;
  22. /**
  23. * The vocabulary storage.
  24. *
  25. * @var \Drupal\taxonomy\VocabularyStorageInterface
  26. */
  27. protected $vocabularyStorage;
  28. /**
  29. * The term storage.
  30. *
  31. * @var \Drupal\taxonomy\TermStorageInterface
  32. */
  33. protected $termStorage;
  34. /**
  35. * Constructs a TaxonomyIndexTid object.
  36. *
  37. * @param array $configuration
  38. * A configuration array containing information about the plugin instance.
  39. * @param string $plugin_id
  40. * The plugin_id for the plugin instance.
  41. * @param mixed $plugin_definition
  42. * The plugin implementation definition.
  43. * @param \Drupal\taxonomy\VocabularyStorageInterface $vocabulary_storage
  44. * The vocabulary storage.
  45. * @param \Drupal\taxonomy\TermStorageInterface $term_storage
  46. * The term storage.
  47. */
  48. public function __construct(array $configuration, $plugin_id, $plugin_definition, VocabularyStorageInterface $vocabulary_storage, TermStorageInterface $term_storage) {
  49. parent::__construct($configuration, $plugin_id, $plugin_definition);
  50. $this->vocabularyStorage = $vocabulary_storage;
  51. $this->termStorage = $term_storage;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  57. return new static(
  58. $configuration,
  59. $plugin_id,
  60. $plugin_definition,
  61. $container->get('entity.manager')->getStorage('taxonomy_vocabulary'),
  62. $container->get('entity.manager')->getStorage('taxonomy_term')
  63. );
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
  69. parent::init($view, $display, $options);
  70. if (!empty($this->definition['vocabulary'])) {
  71. $this->options['vid'] = $this->definition['vocabulary'];
  72. }
  73. }
  74. public function hasExtraOptions() { return TRUE; }
  75. /**
  76. * {@inheritdoc}
  77. */
  78. public function getValueOptions() {
  79. return $this->valueOptions;
  80. }
  81. protected function defineOptions() {
  82. $options = parent::defineOptions();
  83. $options['type'] = array('default' => 'textfield');
  84. $options['limit'] = array('default' => TRUE);
  85. $options['vid'] = array('default' => '');
  86. $options['hierarchy'] = array('default' => FALSE);
  87. $options['error_message'] = array('default' => TRUE);
  88. return $options;
  89. }
  90. public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
  91. $vocabularies = $this->vocabularyStorage->loadMultiple();
  92. $options = array();
  93. foreach ($vocabularies as $voc) {
  94. $options[$voc->id()] = $voc->label();
  95. }
  96. if ($this->options['limit']) {
  97. // We only do this when the form is displayed.
  98. if (empty($this->options['vid'])) {
  99. $first_vocabulary = reset($vocabularies);
  100. $this->options['vid'] = $first_vocabulary->id();
  101. }
  102. if (empty($this->definition['vocabulary'])) {
  103. $form['vid'] = array(
  104. '#type' => 'radios',
  105. '#title' => $this->t('Vocabulary'),
  106. '#options' => $options,
  107. '#description' => $this->t('Select which vocabulary to show terms for in the regular options.'),
  108. '#default_value' => $this->options['vid'],
  109. );
  110. }
  111. }
  112. $form['type'] = array(
  113. '#type' => 'radios',
  114. '#title' => $this->t('Selection type'),
  115. '#options' => array('select' => $this->t('Dropdown'), 'textfield' => $this->t('Autocomplete')),
  116. '#default_value' => $this->options['type'],
  117. );
  118. $form['hierarchy'] = array(
  119. '#type' => 'checkbox',
  120. '#title' => $this->t('Show hierarchy in dropdown'),
  121. '#default_value' => !empty($this->options['hierarchy']),
  122. '#states' => array(
  123. 'visible' => array(
  124. ':input[name="options[type]"]' => array('value' => 'select'),
  125. ),
  126. ),
  127. );
  128. }
  129. protected function valueForm(&$form, FormStateInterface $form_state) {
  130. $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
  131. if (empty($vocabulary) && $this->options['limit']) {
  132. $form['markup'] = array(
  133. '#markup' => '<div class="js-form-item form-item">' . $this->t('An invalid vocabulary is selected. Please change it in the options.') . '</div>',
  134. );
  135. return;
  136. }
  137. if ($this->options['type'] == 'textfield') {
  138. $terms = $this->value ? Term::loadMultiple(($this->value)) : array();
  139. $form['value'] = array(
  140. '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->label())) : $this->t('Select terms'),
  141. '#type' => 'textfield',
  142. '#default_value' => EntityAutocomplete::getEntityLabels($terms),
  143. );
  144. if ($this->options['limit']) {
  145. $form['value']['#type'] = 'entity_autocomplete';
  146. $form['value']['#target_type'] = 'taxonomy_term';
  147. $form['value']['#selection_settings']['target_bundles'] = array($vocabulary->id());
  148. $form['value']['#tags'] = TRUE;
  149. $form['value']['#process_default_value'] = FALSE;
  150. }
  151. }
  152. else {
  153. if (!empty($this->options['hierarchy']) && $this->options['limit']) {
  154. $tree = $this->termStorage->loadTree($vocabulary->id(), 0, NULL, TRUE);
  155. $options = array();
  156. if ($tree) {
  157. foreach ($tree as $term) {
  158. $choice = new \stdClass();
  159. $choice->option = array($term->id() => str_repeat('-', $term->depth) . \Drupal::entityManager()->getTranslationFromContext($term)->label());
  160. $options[] = $choice;
  161. }
  162. }
  163. }
  164. else {
  165. $options = array();
  166. $query = \Drupal::entityQuery('taxonomy_term')
  167. // @todo Sorting on vocabulary properties -
  168. // https://www.drupal.org/node/1821274.
  169. ->sort('weight')
  170. ->sort('name')
  171. ->addTag('taxonomy_term_access');
  172. if ($this->options['limit']) {
  173. $query->condition('vid', $vocabulary->id());
  174. }
  175. $terms = Term::loadMultiple($query->execute());
  176. foreach ($terms as $term) {
  177. $options[$term->id()] = \Drupal::entityManager()->getTranslationFromContext($term)->label();
  178. }
  179. }
  180. $default_value = (array) $this->value;
  181. if ($exposed = $form_state->get('exposed')) {
  182. $identifier = $this->options['expose']['identifier'];
  183. if (!empty($this->options['expose']['reduce'])) {
  184. $options = $this->reduceValueOptions($options);
  185. if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
  186. $default_value = array();
  187. }
  188. }
  189. if (empty($this->options['expose']['multiple'])) {
  190. if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
  191. $default_value = 'All';
  192. }
  193. elseif (empty($default_value)) {
  194. $keys = array_keys($options);
  195. $default_value = array_shift($keys);
  196. }
  197. // Due to #1464174 there is a chance that array('') was saved in the admin ui.
  198. // Let's choose a safe default value.
  199. elseif ($default_value == array('')) {
  200. $default_value = 'All';
  201. }
  202. else {
  203. $copy = $default_value;
  204. $default_value = array_shift($copy);
  205. }
  206. }
  207. }
  208. $form['value'] = array(
  209. '#type' => 'select',
  210. '#title' => $this->options['limit'] ? $this->t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->label())) : $this->t('Select terms'),
  211. '#multiple' => TRUE,
  212. '#options' => $options,
  213. '#size' => min(9, count($options)),
  214. '#default_value' => $default_value,
  215. );
  216. $user_input = $form_state->getUserInput();
  217. if ($exposed && isset($identifier) && !isset($user_input[$identifier])) {
  218. $user_input[$identifier] = $default_value;
  219. $form_state->setUserInput($user_input);
  220. }
  221. }
  222. if (!$form_state->get('exposed')) {
  223. // Retain the helper option
  224. $this->helper->buildOptionsForm($form, $form_state);
  225. // Show help text if not exposed to end users.
  226. $form['value']['#description'] = t('Leave blank for all. Otherwise, the first selected term will be the default instead of "Any".');
  227. }
  228. }
  229. protected function valueValidate($form, FormStateInterface $form_state) {
  230. // We only validate if they've chosen the text field style.
  231. if ($this->options['type'] != 'textfield') {
  232. return;
  233. }
  234. $tids = array();
  235. if ($values = $form_state->getValue(array('options', 'value'))) {
  236. foreach ($values as $value) {
  237. $tids[] = $value['target_id'];
  238. }
  239. }
  240. $form_state->setValue(array('options', 'value'), $tids);
  241. }
  242. public function acceptExposedInput($input) {
  243. if (empty($this->options['exposed'])) {
  244. return TRUE;
  245. }
  246. // We need to know the operator, which is normally set in
  247. // \Drupal\views\Plugin\views\filter\FilterPluginBase::acceptExposedInput(),
  248. // before we actually call the parent version of ourselves.
  249. if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
  250. $this->operator = $input[$this->options['expose']['operator_id']];
  251. }
  252. // If view is an attachment and is inheriting exposed filters, then assume
  253. // exposed input has already been validated
  254. if (!empty($this->view->is_attachment) && $this->view->display_handler->usesExposed()) {
  255. $this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
  256. }
  257. // If we're checking for EMPTY or NOT, we don't need any input, and we can
  258. // say that our input conditions are met by just having the right operator.
  259. if ($this->operator == 'empty' || $this->operator == 'not empty') {
  260. return TRUE;
  261. }
  262. // If it's non-required and there's no value don't bother filtering.
  263. if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
  264. return FALSE;
  265. }
  266. $rc = parent::acceptExposedInput($input);
  267. if ($rc) {
  268. // If we have previously validated input, override.
  269. if (isset($this->validated_exposed_input)) {
  270. $this->value = $this->validated_exposed_input;
  271. }
  272. }
  273. return $rc;
  274. }
  275. public function validateExposed(&$form, FormStateInterface $form_state) {
  276. if (empty($this->options['exposed'])) {
  277. return;
  278. }
  279. $identifier = $this->options['expose']['identifier'];
  280. // We only validate if they've chosen the text field style.
  281. if ($this->options['type'] != 'textfield') {
  282. if ($form_state->getValue($identifier) != 'All') {
  283. $this->validated_exposed_input = (array) $form_state->getValue($identifier);
  284. }
  285. return;
  286. }
  287. if (empty($this->options['expose']['identifier'])) {
  288. return;
  289. }
  290. if ($values = $form_state->getValue($identifier)) {
  291. foreach ($values as $value) {
  292. $this->validated_exposed_input[] = $value['target_id'];
  293. }
  294. }
  295. }
  296. protected function valueSubmit($form, FormStateInterface $form_state) {
  297. // prevent array_filter from messing up our arrays in parent submit.
  298. }
  299. public function buildExposeForm(&$form, FormStateInterface $form_state) {
  300. parent::buildExposeForm($form, $form_state);
  301. if ($this->options['type'] != 'select') {
  302. unset($form['expose']['reduce']);
  303. }
  304. $form['error_message'] = array(
  305. '#type' => 'checkbox',
  306. '#title' => $this->t('Display error message'),
  307. '#default_value' => !empty($this->options['error_message']),
  308. );
  309. }
  310. public function adminSummary() {
  311. // set up $this->valueOptions for the parent summary
  312. $this->valueOptions = array();
  313. if ($this->value) {
  314. $this->value = array_filter($this->value);
  315. $terms = Term::loadMultiple($this->value);
  316. foreach ($terms as $term) {
  317. $this->valueOptions[$term->id()] = \Drupal::entityManager()->getTranslationFromContext($term)->label();
  318. }
  319. }
  320. return parent::adminSummary();
  321. }
  322. /**
  323. * {@inheritdoc}
  324. */
  325. public function getCacheContexts() {
  326. $contexts = parent::getCacheContexts();
  327. // The result potentially depends on term access and so is just cacheable
  328. // per user.
  329. // @todo See https://www.drupal.org/node/2352175.
  330. $contexts[] = 'user';
  331. return $contexts;
  332. }
  333. /**
  334. * {@inheritdoc}
  335. */
  336. public function calculateDependencies() {
  337. $dependencies = parent::calculateDependencies();
  338. $vocabulary = $this->vocabularyStorage->load($this->options['vid']);
  339. $dependencies[$vocabulary->getConfigDependencyKey()][] = $vocabulary->getConfigDependencyName();
  340. foreach ($this->termStorage->loadMultiple($this->options['value']) as $term) {
  341. $dependencies[$term->getConfigDependencyKey()][] = $term->getConfigDependencyName();
  342. }
  343. return $dependencies;
  344. }
  345. }

Classes

Namesort descending Description
TaxonomyIndexTid Filter by term id.