You are here

taxonomy.install in Drupal 8

Same filename and directory in other branches
  1. 9 core/modules/taxonomy/taxonomy.install

Install, update and uninstall functions for the taxonomy module.

File

core/modules/taxonomy/taxonomy.install
View source
<?php

/**
 * @file
 * Install, update and uninstall functions for the taxonomy module.
 */
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Site\Settings;

/**
 * Implements hook_requirements().
 */
function taxonomy_requirements($phase) {
  $requirements = [];
  if ($phase === 'update') {

    // Check for invalid data before making terms revisionable.

    /** @var \Drupal\Core\Update\UpdateRegistry $registry */
    $registry = \Drupal::service('update.post_update_registry');
    $update_name = 'taxonomy_post_update_make_taxonomy_term_revisionable';
    if (in_array($update_name, $registry
      ->getPendingUpdateFunctions(), TRUE)) {

      // The 'name' field is non-NULL - if we get a NULL value that indicates a
      // failure to join on taxonomy_term_field_data.
      $is_broken = \Drupal::entityQuery('taxonomy_term')
        ->condition('name', NULL, 'IS NULL')
        ->range(0, 1)
        ->accessCheck(FALSE)
        ->execute();
      if ($is_broken) {
        $requirements[$update_name] = [
          'title' => t('Taxonomy term data'),
          'value' => t('Integrity issues detected'),
          'description' => t('The make_taxonomy_term_revisionable database update cannot be run until the data has been fixed. See the <a href=":change_record">change record</a> for more information.', [
            ':change_record' => 'https://www.drupal.org/node/3117753',
          ]),
          'severity' => REQUIREMENT_ERROR,
        ];
      }
    }
  }
  return $requirements;
}

/**
 * Convert the custom taxonomy term hierarchy storage to a default storage.
 */
function taxonomy_update_8501() {
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();

  /** @var \Drupal\Core\Field\BaseFieldDefinition $field_storage_definition */
  $field_storage_definition = $definition_update_manager
    ->getFieldStorageDefinition('parent', 'taxonomy_term');
  $field_storage_definition
    ->setCustomStorage(FALSE);
  $definition_update_manager
    ->updateFieldStorageDefinition($field_storage_definition);
}

/**
 * Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}.
 */
function taxonomy_update_8502(&$sandbox) {
  $database = \Drupal::database();
  if (!isset($sandbox['current'])) {

    // Set batch ops sandbox.
    $sandbox['current'] = 0;
    $sandbox['tid'] = -1;
    $sandbox['delta'] = 0;
    $sandbox['limit'] = Settings::get('entity_update_batch_size', 50);

    // Count records using a join, as there might be orphans in the hierarchy
    // table. See https://www.drupal.org/project/drupal/issues/2997982.
    $select = $database
      ->select('taxonomy_term_hierarchy', 'h');
    $select
      ->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
    $sandbox['max'] = $select
      ->countQuery()
      ->execute()
      ->fetchField();
  }

  // Save the hierarchy.
  $select = $database
    ->select('taxonomy_term_hierarchy', 'h');
  $select
    ->join('taxonomy_term_data', 'd', 'h.tid = d.tid');
  $hierarchy = $select
    ->fields('h', [
    'tid',
    'parent',
  ])
    ->fields('d', [
    'vid',
    'langcode',
  ])
    ->range($sandbox['current'], $sandbox['limit'])
    ->orderBy('tid', 'ASC')
    ->orderBy('parent', 'ASC')
    ->execute()
    ->fetchAll();

  // Restore data.
  $insert = $database
    ->insert('taxonomy_term__parent')
    ->fields([
    'bundle',
    'entity_id',
    'revision_id',
    'langcode',
    'delta',
    'parent_target_id',
  ]);
  foreach ($hierarchy as $row) {
    if ($row->tid !== $sandbox['tid']) {
      $sandbox['delta'] = 0;
      $sandbox['tid'] = $row->tid;
    }
    $insert
      ->values([
      'bundle' => $row->vid,
      'entity_id' => $row->tid,
      'revision_id' => $row->tid,
      'langcode' => $row->langcode,
      'delta' => $sandbox['delta'],
      'parent_target_id' => $row->parent,
    ]);
    $sandbox['delta']++;
    $sandbox['current']++;
  }
  $insert
    ->execute();
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['current'] / $sandbox['max'];
  if ($sandbox['#finished'] >= 1) {

    // Update the entity type because the 'taxonomy_term_hierarchy' table is no
    // longer part of its shared tables schema.
    $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
    $definition_update_manager
      ->updateEntityType($definition_update_manager
      ->getEntityType('taxonomy_term'));

    // \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate()
    // only deletes *known* entity tables (i.e. the base, data and revision
    // tables), so we have to drop it manually.
    $database
      ->schema()
      ->dropTable('taxonomy_term_hierarchy');
    return t('Taxonomy term hierarchy has been converted to default entity reference storage.');
  }
}

/**
 * Update views to use {taxonomy_term__parent} in relationships.
 */
function taxonomy_update_8503() {
  $config_factory = \Drupal::configFactory();
  foreach ($config_factory
    ->listAll('views.view.') as $id) {
    $view = $config_factory
      ->getEditable($id);
    foreach (array_keys($view
      ->get('display')) as $display_id) {
      $changed = FALSE;
      foreach ([
        'relationships',
        'filters',
        'arguments',
      ] as $handler_type) {
        $base_path = "display.{$display_id}.display_options.{$handler_type}";
        $handlers = $view
          ->get($base_path);
        if (!$handlers) {
          continue;
        }
        foreach ($handlers as $handler_key => $handler_config) {
          $table_path = "{$base_path}.{$handler_key}.table";
          $field_path = "{$base_path}.{$handler_key}.field";
          $table = $view
            ->get($table_path);
          $field = $view
            ->get($field_path);
          if ($table && $table === 'taxonomy_term_hierarchy' && ($field && $field === 'parent')) {
            $view
              ->set($table_path, 'taxonomy_term__parent');
            $view
              ->set($field_path, 'parent_target_id');
            $changed = TRUE;
          }
        }
      }
      if ($changed) {
        $view
          ->save(TRUE);
      }
    }
  }
}

/**
 * Add the publishing status fields to taxonomy terms.
 */
function taxonomy_update_8601() {
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  $entity_type = $definition_update_manager
    ->getEntityType('taxonomy_term');

  // Bail out early if a field named 'status' is already installed.
  if ($definition_update_manager
    ->getFieldStorageDefinition('status', 'taxonomy_term')) {
    $message = \Drupal::state()
      ->get('taxonomy_update_8601_skip_message', t('The publishing status field has <strong>not</strong> been added to taxonomy terms. See <a href=":link">this page</a> for more information on how to install it.', [
      ':link' => 'https://www.drupal.org/node/2985366',
    ]));
    return $message;
  }

  // Add the 'published' entity key to the taxonomy_term entity type.
  $entity_keys = $entity_type
    ->getKeys();
  $entity_keys['published'] = 'status';
  $entity_type
    ->set('entity_keys', $entity_keys);
  $definition_update_manager
    ->updateEntityType($entity_type);

  // Add the status field.
  $status = BaseFieldDefinition::create('boolean')
    ->setLabel(t('Publishing status'))
    ->setDescription(t('A boolean indicating the published state.'))
    ->setRevisionable(TRUE)
    ->setTranslatable(TRUE)
    ->setDefaultValue(TRUE);
  $has_content_translation_status_field = $definition_update_manager
    ->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
  if ($has_content_translation_status_field) {
    $status
      ->setInitialValueFromField('content_translation_status', TRUE);
  }
  else {
    $status
      ->setInitialValue(TRUE);
  }
  $definition_update_manager
    ->installFieldStorageDefinition('status', 'taxonomy_term', 'taxonomy_term', $status);

  // Uninstall the 'content_translation_status' field if needed.
  if ($has_content_translation_status_field) {
    $content_translation_status = $definition_update_manager
      ->getFieldStorageDefinition('content_translation_status', 'taxonomy_term');
    $definition_update_manager
      ->uninstallFieldStorageDefinition($content_translation_status);
  }
  return t('The publishing status field has been added to taxonomy terms.');
}

/**
 * Add an index on the 'taxonomy_term__parent' field table.
 */
function taxonomy_update_8701() {
  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  $storage_definition = $entity_definition_update_manager
    ->getFieldStorageDefinition('parent', 'taxonomy_term');
  $entity_definition_update_manager
    ->updateFieldStorageDefinition($storage_definition);
}

/**
 * Fix the parent field langcode data.
 */
function taxonomy_update_8702(&$sandbox) {
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  $field_storage_definition = $definition_update_manager
    ->getFieldStorageDefinition('parent', 'taxonomy_term');
  $entity_type = $definition_update_manager
    ->getEntityType('taxonomy_term');

  // Only perform the update if:
  // - The field is not translatable. It's possible that custom or contrib code
  //   has overridden this.
  // - The field is not revisionable. If it is then
  //   taxonomy_post_update_make_taxonomy_term_revisionable() has already run
  //   and this used to fix the parent field langcode data.
  // - Terms are using a SQL-based storage class.
  if (!$field_storage_definition
    ->isTranslatable() && !$entity_type
    ->isRevisionable() && is_subclass_of($entity_type
    ->getStorageClass(), SqlEntityStorageInterface::class)) {

    // taxonomy_update_8502() populated the langcode field of
    // 'taxonomy_term__parent' using the term's langcode. However, the field is
    // not translatable and, therefore, should use the term's default language.
    $database = \Drupal::database();
    $select = $database
      ->select('taxonomy_term__parent', 'tp');
    $select
      ->join('taxonomy_term_field_data', 'tdf', 'tp.entity_id = tdf.tid AND tdf.langcode <> tp.langcode');
    $select
      ->fields('tp', [
      'entity_id',
    ])
      ->fields('tdf', [
      'tid',
      'langcode',
    ])
      ->condition('tdf.default_langcode', 1);
    if (!isset($sandbox['max'])) {
      $count_query = clone $select;
      $sandbox['max'] = $count_query
        ->countQuery()
        ->execute()
        ->fetchField();
      $sandbox['current'] = 0;
    }
    $result = $select
      ->execute();
    $processed = 0;
    while ($row = $result
      ->fetchAssoc()) {
      $database
        ->update('taxonomy_term__parent')
        ->condition('entity_id', $row['tid'])
        ->fields([
        'langcode' => $row['langcode'],
      ])
        ->execute();
      $sandbox['current']++;
      $processed++;
      if ($processed >= Settings::get('entity_update_batch_size', 50)) {
        break;
      }
    }
  }
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['current'] / $sandbox['max'];
}

Functions

Namesort descending Description
taxonomy_requirements Implements hook_requirements().
taxonomy_update_8501 Convert the custom taxonomy term hierarchy storage to a default storage.
taxonomy_update_8502 Copy hierarchy from {taxonomy_term_hierarchy} to {taxonomy_term__parent}.
taxonomy_update_8503 Update views to use {taxonomy_term__parent} in relationships.
taxonomy_update_8601 Add the publishing status fields to taxonomy terms.
taxonomy_update_8701 Add an index on the 'taxonomy_term__parent' field table.
taxonomy_update_8702 Fix the parent field langcode data.