taxonomy.module 22.5 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
<?php

/**
 * @file
 * Enables the organization of content into categories.
 */

use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\taxonomy\TermInterface;
use Drupal\taxonomy\VocabularyInterface;

/**
 * Denotes that no term in the vocabulary has a parent.
 */
const TAXONOMY_HIERARCHY_DISABLED = 0;

/**
 * Denotes that one or more terms in the vocabulary has a single parent.
 */
const TAXONOMY_HIERARCHY_SINGLE = 1;

/**
 * Denotes that one or more terms in the vocabulary have multiple parents.
 */
const TAXONOMY_HIERARCHY_MULTIPLE = 2;

/**
 * Implements hook_help().
 */
function taxonomy_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.taxonomy':
      $field_ui_url = \Drupal::moduleHandler()->moduleExists('field_ui') ? \Drupal::url('help.page', array('name' => 'field_ui')) : '#';
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Taxonomy module allows users who have permission to create and edit content to categorize (tag) content of that type. Users who have the <em>Administer vocabularies and terms</em> <a href=":permissions" title="Taxonomy module permissions">permission</a> can add <em>vocabularies</em> that contain a set of related <em>terms</em>. The terms in a vocabulary can either be pre-set by an administrator or built gradually as content is added and edited. Terms may be organized hierarchically if desired.', array(':permissions' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-taxonomy')))) . '</p>';
      $output .= '<p>' . t('For more information, see the <a href=":taxonomy">online documentation for the Taxonomy module</a>.', array(':taxonomy' => 'https://www.drupal.org/documentation/modules/taxonomy/')) . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Managing vocabularies') . '</dt>';
      $output .= '<dd>' . t('Users who have the <em>Administer vocabularies and terms</em> permission can add and edit vocabularies from the <a href=":taxonomy_admin">Taxonomy administration page</a>. Vocabularies can be deleted from their <em>Edit vocabulary</em> page. Users with the <em>Taxonomy term: Administer fields</em> permission may add additional fields for terms in that vocabulary using the <a href=":field_ui">Field UI module</a>.' , array(':taxonomy_admin' => \Drupal::url('entity.taxonomy_vocabulary.collection'), ':field_ui' => $field_ui_url)) . '</dd>';
      $output .= '<dt>' . t('Managing terms') . '</dt>';
      $output .= '<dd>' . t('Users who have the <em>Administer vocabularies and terms</em> permission or the <em>Edit terms</em> permission for a particular vocabulary can add, edit, and organize the terms in a vocabulary from a vocabulary\'s term listing page, which can be accessed by going to the <a href=":taxonomy_admin">Taxonomy administration page</a> and clicking <em>List terms</em> in the <em>Operations</em> column. Users must have the <em>Administer vocabularies and terms</em> permission or the <em>Delete terms</em> permission for a particular vocabulary to delete terms.' , array(':taxonomy_admin' => \Drupal::url('entity.taxonomy_vocabulary.collection'))) . ' </dd>';
      $output .= '<dt>' . t('Classifying entity content') . '</dt>';
      $output .= '<dd>' . t('A user with the <em>Administer fields</em> permission for a certain entity type may add <em>Taxonomy term</em> reference fields to the entity type, which will allow entities to be classified using taxonomy terms. See the <a href=":entity_reference">Entity Reference help</a> for more information about reference fields. See the <a href=":field">Field module help</a> and the <a href=":field_ui">Field UI help</a> pages for general information on fields and how to create and manage them.' , array(':field_ui' => $field_ui_url, ':field' => \Drupal::url('help.page', array('name' => 'field')), ':entity_reference' => \Drupal::url('help.page', array('name' => 'entity_reference')))) . '</dd>';
      $output .= '<dt>' . t('Adding new terms during content creation') . '</dt>';
      $output .= '<dd>' . t('Allowing users to add new terms gradually builds a vocabulary as content is added and edited. Users can add new terms if either of the two <em>Autocomplete</em> widgets is chosen for the Taxonomy term reference field in the <em>Manage form display</em> page for the field. You will also need to enable the <em>Create referenced entities if they don\'t already exist</em> option, and restrict the field to one vocabulary.') . '</dd>';
      $output .= '<dt>' . t('Configuring displays and form displays') . '</dt>';
      $output .= '<dd>' . t('See the <a href=":entity_reference">Entity Reference help</a> page for the field widgets and formatters that can be configured for any reference field on the <em>Manage display</em> and <em>Manage form display</em> pages. Taxonomy additionally provides an <em>RSS category</em> formatter that displays nothing when the entity item is displayed as HTML, but displays an RSS category instead of a list when the entity item is displayed in an RSS feed.', array(':entity_reference' => \Drupal::url('help.page', array('name' => 'entity_reference')))) . '</li>';
      $output .= '</ul>';
      $output .= '</dd>';
      $output .= '</dl>';
      return $output;

    case 'entity.taxonomy_vocabulary.collection':
      $output = '<p>' . t('Taxonomy is for categorizing content. Terms are grouped into vocabularies. For example, a vocabulary called "Fruit" would contain the terms "Apple" and "Banana".') . '</p>';
      return $output;

    case 'entity.taxonomy_vocabulary.overview_form':
      $vocabulary = $route_match->getParameter('taxonomy_vocabulary');
      switch ($vocabulary->getHierarchy()) {
        case TAXONOMY_HIERARCHY_DISABLED:
          return '<p>' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '</p>';
        case TAXONOMY_HIERARCHY_SINGLE:
          return '<p>' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '</p>';
        case TAXONOMY_HIERARCHY_MULTIPLE:
          return '<p>' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()))) . '</p>';
      }
  }
}

/**
 * Entity URI callback.
 */
function taxonomy_term_uri($term) {
  return new Url('entity.taxonomy_term.canonical', array(
    'taxonomy_term' => $term->id(),
  ));
}

/**
 * Implements hook_page_attachments_alter().
 */
function taxonomy_page_attachments_alter(array &$page) {
  $route_match = \Drupal::routeMatch();
  if ($route_match->getRouteName() == 'entity.taxonomy_term.canonical' && ($term = $route_match->getParameter('taxonomy_term')) && $term instanceof TermInterface) {
    foreach ($term->uriRelationships() as $rel) {
      // Set the URI relationships, like canonical.
      $page['#attached']['html_head_link'][] = array(
        array(
          'rel' => $rel,
          'href' => $term->url($rel),
        ),
        TRUE,
      );

      // Set the term path as the canonical URL to prevent duplicate content.
      if ($rel == 'canonical') {
        // Set the non-aliased canonical path as a default shortlink.
        $page['#attached']['html_head_link'][] = array(
          array(
            'rel' => 'shortlink',
            'href' => $term->url($rel, array('alias' => TRUE)),
          ),
          TRUE,
        );
      }
    }
  }
}

/**
 * Implements hook_theme().
 */
function taxonomy_theme() {
  return array(
    'taxonomy_term' => array(
      'render element' => 'elements',
    ),
  );
}

/**
 * Checks and updates the hierarchy flag of a vocabulary.
 *
 * Checks the current parents of all terms in a vocabulary and updates the
 * vocabulary's hierarchy setting to the lowest possible level. If no term
 * has parent terms then the vocabulary will be given a hierarchy of
 * TAXONOMY_HIERARCHY_DISABLED. If any term has a single parent then the
 * vocabulary will be given a hierarchy of TAXONOMY_HIERARCHY_SINGLE. If any
 * term has multiple parents then the vocabulary will be given a hierarchy of
 * TAXONOMY_HIERARCHY_MULTIPLE.
 *
 * @param \Drupal\taxonomy\VocabularyInterface $vocabulary
 *   A taxonomy vocabulary entity.
 * @param $changed_term
 *   An array of the term structure that was updated.
 *
 * @return
 *   An integer that represents the level of the vocabulary's hierarchy.
 */
function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) {
  $tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vocabulary->id());
  $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
  foreach ($tree as $term) {
    // Update the changed term with the new parent value before comparison.
    if ($term->tid == $changed_term['tid']) {
      $term = (object) $changed_term;
      $term->parents = $term->parent;
    }
    // Check this term's parent count.
    if (count($term->parents) > 1) {
      $hierarchy = TAXONOMY_HIERARCHY_MULTIPLE;
      break;
    }
    elseif (count($term->parents) == 1 && !isset($term->parents[0])) {
      $hierarchy = TAXONOMY_HIERARCHY_SINGLE;
    }
  }
  if ($hierarchy != $vocabulary->getHierarchy()) {
    $vocabulary->setHierarchy($hierarchy);
    $vocabulary->save();
  }

  return $hierarchy;
}

/**
 * Generates an array which displays a term detail page.
 *
 * @param \Drupal\taxonomy\Entity\Term $term
 *   A taxonomy term object.
 * @param string $view_mode
 *   View mode; e.g., 'full', 'teaser', etc.
 * @param string $langcode
 *   (optional) A language code to use for rendering. Defaults to the global
 *   content language of the current request.
 *
 * @return array
 *   A $page element suitable for use by drupal_render().
 */
function taxonomy_term_view(Term $term, $view_mode = 'full', $langcode = NULL) {
  return entity_view($term, $view_mode, $langcode);
}

/**
 * Constructs a drupal_render() style array from an array of loaded terms.
 *
 * @param array $terms
 *   An array of taxonomy terms as returned by Term::loadMultiple().
 * @param string $view_mode
 *   View mode; e.g., 'full', 'teaser', etc.
 * @param string $langcode
 *   (optional) A language code to use for rendering. Defaults to the global
 *   content language of the current request.
 *
 * @return array
 *   An array in the format expected by drupal_render().
 */
function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcode = NULL) {
  return entity_view_multiple($terms, $view_mode, $langcode);
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function taxonomy_theme_suggestions_taxonomy_term(array $variables) {
  $suggestions = array();

  /** @var \Drupal\taxonomy\TermInterface $term */
  $term = $variables['elements']['#taxonomy_term'];

  $suggestions[] = 'taxonomy_term__' . $term->bundle();
  $suggestions[] = 'taxonomy_term__' . $term->id();

  return $suggestions;
}

/**
 * Prepares variables for taxonomy term templates.
 *
 * Default template: taxonomy-term.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing the taxonomy term and any
 *     fields attached to the term. Properties used:
 *     - #taxonomy_term: A \Drupal\taxonomy\TermInterface object.
 *     - #view_mode: The current view mode for this taxonomy term, e.g.
 *       'full' or 'teaser'.
 *   - attributes: HTML attributes for the containing element.
 */
function template_preprocess_taxonomy_term(&$variables) {
  $variables['view_mode'] = $variables['elements']['#view_mode'];
  $variables['term'] = $variables['elements']['#taxonomy_term'];
  /** @var \Drupal\taxonomy\TermInterface $term */
  $term = $variables['term'];

  $variables['url'] = $term->url();
  // We use name here because that is what appears in the UI.
  $variables['name'] = $variables['elements']['name'];
  unset($variables['elements']['name']);
  $variables['page'] = $variables['view_mode'] == 'full' && taxonomy_term_is_page($term);

  // Helpful $content variable for templates.
  $variables['content'] = array();
  foreach (Element::children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
}

/**
 * Returns whether the current page is the page of the passed-in term.
 *
 * @param \Drupal\taxonomy\Entity\Term $term
 *   A taxonomy term entity.
 */
function taxonomy_term_is_page(Term $term) {
  if (\Drupal::routeMatch()->getRouteName() == 'entity.taxonomy_term.canonical' && $page_term_id = \Drupal::routeMatch()->getRawParameter('taxonomy_term')) {
    return $page_term_id == $term->id();
  }
  return FALSE;
}

/**
 * Clear all static cache variables for terms.
 */
function taxonomy_terms_static_reset() {
  \Drupal::entityManager()->getStorage('taxonomy_term')->resetCache();
}

/**
 * Clear all static cache variables for vocabularies.
 *
 * @param $ids
 *   An array of ids to reset in the entity cache.
 */
function taxonomy_vocabulary_static_reset(array $ids = NULL) {
  \Drupal::entityManager()->getStorage('taxonomy_vocabulary')->resetCache($ids);
}

/**
 * Get names for all taxonomy vocabularies.
 *
 * @return array
 *   A list of existing vocabulary IDs.
 */
function taxonomy_vocabulary_get_names() {
  $names = &drupal_static(__FUNCTION__);

  if (!isset($names)) {
    $names = array();
    $config_names = \Drupal::configFactory()->listAll('taxonomy.vocabulary.');
    foreach ($config_names as $config_name) {
      $id = substr($config_name, strlen('taxonomy.vocabulary.'));
      $names[$id] = $id;
    }
  }

  return $names;
}

/**
 * Try to map a string to an existing term, as for glossary use.
 *
 * Provides a case-insensitive and trimmed mapping, to maximize the
 * likelihood of a successful match.
 *
 * @param $name
 *   Name of the term to search for.
 * @param $vocabulary
 *   (optional) Vocabulary machine name to limit the search. Defaults to NULL.
 *
 * @return
 *   An array of matching term objects.
 */
function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) {
  $values = array('name' => trim($name));
  if (isset($vocabulary)) {
    $vocabularies = taxonomy_vocabulary_get_names();
    if (isset($vocabularies[$vocabulary])){
      $values['vid'] = $vocabulary;
    }
    else {
      // Return an empty array when filtering by a non-existing vocabulary.
      return array();
    }
  }
  return entity_load_multiple_by_properties('taxonomy_term', $values);
}

/**
 * Load multiple taxonomy terms based on certain conditions.
 *
 * This function should be used whenever you need to load more than one term
 * from the database. Terms are loaded into memory and will not require
 * database access if loaded again during the same page request.
 *
 * @see entity_load_multiple()
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\taxonomy\Entity\Term::loadMultiple().
 *
 * @param array $tids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
 *
 * @return array
 *   An array of taxonomy term entities, indexed by tid. When no results are
 *   found, an empty array is returned.
 */
function taxonomy_term_load_multiple(array $tids = NULL) {
  return Term::loadMultiple($tids);
}

/**
 * Loads multiple taxonomy vocabularies based on certain conditions.
 *
 * This function should be used whenever you need to load more than one
 * vocabulary from the database. Terms are loaded into memory and will not
 * require database access if loaded again during the same page request.
 *
 * @see entity_load_multiple()
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\taxonomy\Entity\Vocabulary::loadMultiple().
 *
 * @param array $vids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
 *
 * @return array
 *  An array of vocabulary objects, indexed by vid.
 */
function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
  return Vocabulary::loadMultiple($vids);
}

/**
 * Return the taxonomy vocabulary entity matching a vocabulary ID.
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\taxonomy\Entity\Vocabulary::load().
 *
 * @param int $vid
 *   The vocabulary's ID.
 *
 * @return \Drupal\taxonomy\Entity\Vocabulary|null
 *   The taxonomy vocabulary entity, if exists, NULL otherwise. Results are
 *   statically cached.
 */
function taxonomy_vocabulary_load($vid) {
  return Vocabulary::load($vid);
}

/**
 * Return the taxonomy term entity matching a term ID.
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\taxonomy\Entity\Term::load().
 *
 * @param $tid
 *   A term's ID
 *
 * @return \Drupal\taxonomy\Entity\Term|null
 *   A taxonomy term entity, or NULL if the term was not found. Results are
 *   statically cached.
 */
function taxonomy_term_load($tid) {
  if (!is_numeric($tid)) {
    return NULL;
  }
  return Term::load($tid);
}

/**
 * Implodes a list of tags of a certain vocabulary into a string.
 *
 * @see \Drupal\Component\Utility\Tags::explode()
 */
function taxonomy_implode_tags($tags, $vid = NULL) {
  $typed_tags = array();
  foreach ($tags as $tag) {
    // Extract terms belonging to the vocabulary in question.
    if (!isset($vid) || $tag->bundle() == $vid) {
      // Make sure we have a completed loaded taxonomy term.
      if ($tag instanceof EntityInterface && $label = $tag->label()) {
        // Commas and quotes in tag names are special cases, so encode 'em.
        $typed_tags[] = Tags::encode($label);
      }
    }
  }
  return implode(', ', $typed_tags);
}

/**
 * Title callback for term pages.
 *
 * @param \Drupal\taxonomy\Entity\Term $term
 *   A taxonomy term entity.
 *
 * @return
 *   The term name to be used as the page title.
 */
function taxonomy_term_title(Term $term) {
  return $term->getName();
}

/**
 * @defgroup taxonomy_index Taxonomy indexing
 * @{
 * Functions to maintain taxonomy indexing.
 *
 * Taxonomy uses default field storage to store canonical relationships
 * between terms and fieldable entities. However its most common use case
 * requires listing all content associated with a term or group of terms
 * sorted by creation date. To avoid slow queries due to joining across
 * multiple node and field tables with various conditions and order by criteria,
 * we maintain a denormalized table with all relationships between terms,
 * published nodes and common sort criteria such as status, sticky and created.
 * When using other field storage engines or alternative methods of
 * denormalizing this data you should set the
 * taxonomy.settings:maintain_index_table to '0' to avoid unnecessary writes in
 * SQL.
 */

/**
 * Implements hook_ENTITY_TYPE_insert() for node entities.
 */
function taxonomy_node_insert(EntityInterface $node) {
  // Add taxonomy index entries for the node.
  taxonomy_build_node_index($node);
}

/**
 * Builds and inserts taxonomy index entries for a given node.
 *
 * The index lists all terms that are related to a given node entity, and is
 * therefore maintained at the entity level.
 *
 * @param \Drupal\node\Entity\Node $node
 *   The node entity.
 */
function taxonomy_build_node_index($node) {
  // We maintain a denormalized table of term/node relationships, containing
  // only data for current, published nodes.
  if (!\Drupal::config('taxonomy.settings')->get('maintain_index_table') || !(\Drupal::entityManager()->getStorage('node') instanceof SqlContentEntityStorage)) {
    return;
  }

  $status = $node->isPublished();
  $sticky = (int) $node->isSticky();
  // We only maintain the taxonomy index for published nodes.
  if ($status && $node->isDefaultRevision()) {
    // Collect a unique list of all the term IDs from all node fields.
    $tid_all = array();
    $entity_reference_class = 'Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem';
    foreach ($node->getFieldDefinitions() as $field) {
      $field_name = $field->getName();
      $class = $field->getItemDefinition()->getClass();
      $is_entity_reference_class = ($class === $entity_reference_class) || is_subclass_of($class, $entity_reference_class);
      if ($is_entity_reference_class && $field->getSetting('target_type') == 'taxonomy_term') {
        foreach ($node->getTranslationLanguages() as $language) {
          foreach ($node->getTranslation($language->getId())->$field_name as $item) {
            if (!$item->isEmpty()) {
              $tid_all[$item->target_id] = $item->target_id;
            }
          }
        }
      }
    }
    // Insert index entries for all the node's terms.
    if (!empty($tid_all)) {
      foreach ($tid_all as $tid) {
        db_merge('taxonomy_index')
          ->key(array('nid' => $node->id(), 'tid' => $tid, 'status' => $node->isPublished()))
          ->fields(array('sticky' => $sticky, 'created' => $node->getCreatedTime()))
          ->execute();
      }
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_update() for node entities.
 */
function taxonomy_node_update(EntityInterface $node) {
  // Always rebuild the node's taxonomy index entries on node save.
  taxonomy_delete_node_index($node);
  taxonomy_build_node_index($node);
}

/**
 * Implements hook_ENTITY_TYPE_predelete() for node entities.
 */
function taxonomy_node_predelete(EntityInterface $node) {
  // Clean up the {taxonomy_index} table when nodes are deleted.
  taxonomy_delete_node_index($node);
}

/**
 * Deletes taxonomy index entries for a given node.
 *
 * @param \Drupal\Core\Entity\EntityInterface $node
 *   The node entity.
 */
function taxonomy_delete_node_index(EntityInterface $node) {
  if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
    db_delete('taxonomy_index')->condition('nid', $node->id())->execute();
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete() for taxonomy_term entities.
 */
function taxonomy_taxonomy_term_delete(Term $term) {
  if (\Drupal::config('taxonomy.settings')->get('maintain_index_table')) {
    // Clean up the {taxonomy_index} table when terms are deleted.
    db_delete('taxonomy_index')->condition('tid', $term->id())->execute();
  }
}

/**
 * @} End of "defgroup taxonomy_index".
 */