You are here

entity_usage.install in Entity Usage 8.3

Install, update and uninstall functions for entity_usage module.

File

entity_usage.install
View source
<?php

/**
 * @file
 * Install, update and uninstall functions for entity_usage module.
 */
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\entity_usage\Controller\ListUsageController;
use Drupal\entity_usage\EntityUsageSourceLevel;

/**
 * Implements hook_schema().
 */
function entity_usage_schema() {
  $schema['entity_usage'] = [
    'description' => 'Track entities that reference other entities.',
    'fields' => [
      'target_id' => [
        'description' => 'The target entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'target_id_string' => [
        'description' => 'The target ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'target_type' => [
        'description' => 'The target entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_id' => [
        'description' => 'The source entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'source_id_string' => [
        'description' => 'The source ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => FALSE,
      ],
      'source_type' => [
        'description' => 'The source entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_langcode' => [
        'description' => 'The source entity language code.',
        'type' => 'varchar_ascii',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_vid' => [
        'description' => 'The source entity revision ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
    ],
    'primary key' => [
      'target_id',
      'target_id_string',
      'target_type',
      'source_id',
      'source_type',
      'source_langcode',
      'source_vid',
    ],
    'indexes' => [
      'target_entity' => [
        'target_type',
        'target_id',
      ],
      'target_entity_string' => [
        'target_type',
        'target_id_string',
      ],
      'source_entity' => [
        'source_type',
        'source_id',
      ],
      'source_entity_string' => [
        'source_type',
        'source_id_string',
      ],
    ],
  ];
  return $schema;
}

/**
 * Implements hook_install().
 */
function entity_usage_install() {

  // Start with 'node' and 'media' entities having the usage tabs enabled.
  $entity_usage_config = \Drupal::configFactory()
    ->getEditable('entity_usage.settings');
  $tabs_enabled = $entity_usage_config
    ->get('local_task_enabled_entity_types');
  $module_handler = \Drupal::moduleHandler();
  $modified = FALSE;
  if ($module_handler
    ->moduleExists('node') && !in_array('node', $tabs_enabled)) {
    $tabs_enabled[] = 'node';
    $modified = TRUE;
  }
  if ($module_handler
    ->moduleExists('media') && !in_array('media', $tabs_enabled)) {
    $tabs_enabled[] = 'media';
    $modified = TRUE;
  }
  if ($modified) {
    $entity_usage_config
      ->set('local_task_enabled_entity_types', $tabs_enabled)
      ->save(TRUE);
  }

  // Start with all top-level types being tracked as source.
  $top_level_types = EntityUsageSourceLevel::getTopLevelEntityTypes();
  if (!empty($top_level_types)) {
    $entity_usage_config
      ->set('track_enabled_source_entity_types', $top_level_types)
      ->save(TRUE);
  }
}

/**
 * Implements hook_requirements().
 */
function entity_usage_requirements($phase) {
  $requirements = [];
  if ($phase === 'runtime') {
    $needs_regenaration = \Drupal::state()
      ->get('entity_usage_needs_regeneration');
    if ($needs_regenaration) {
      $requirements['entity_usage_needs_regeneration'] = [
        'title' => t('Entity Usage'),
        'description' => t('Entity Usage has detected that usage statistics may be stale. Please visit the <a href="@batch_url">batch update</a> page and regenerate your statistics.', [
          '@batch_url' => Url::fromRoute('entity_usage.batch_update')
            ->toString(),
        ]),
        'severity' => REQUIREMENT_ERROR,
      ];
    }
  }
  return $requirements;
}

/**
 * Include "method" also as primary key for the {entity_usage} table.
 */
function entity_usage_update_8001(&$sandbox) {
  $database = \Drupal::database();
  $database
    ->schema()
    ->dropPrimaryKey('entity_usage');
  $new_primary_keys = [
    't_id',
    't_type',
    're_id',
    're_type',
    'method',
  ];
  $database
    ->schema()
    ->addPrimaryKey('entity_usage', $new_primary_keys);
}

/**
 * Recreate the entity usage table with the new schema.
 */
function entity_usage_update_8201(&$sandbox) {
  $schema = \Drupal::database()
    ->schema();
  $schema
    ->dropTable('entity_usage');
  $new_table_schema = entity_usage_schema();
  $schema
    ->createTable('entity_usage', $new_table_schema['entity_usage']);
}

/**
 * Trigger entity usage statistics in the new schema.
 */
function entity_usage_update_8202(&$sandbox) {

  // This flag is here only to ensure that sites that have already executed
  // update 8202 will not run entity_usage_post_update_regenerate_2x() again.
  \Drupal::state()
    ->set('entity_usage_2x_regenerate', TRUE);
}

/**
 * Re-generate entity_usage statistics.
 */
function entity_usage_post_update_regenerate_2x(&$sandbox) {
  if (!\Drupal::state()
    ->get('entity_usage_2x_regenerate')) {
    return;
  }

  // First pass.
  if (empty($sandbox['total'])) {
    $sandbox['current_key'] = 0;
    $sandbox['total'] = 0;
    $sandbox['entities'] = [];
    $to_track = \Drupal::config('entity_usage.settings')
      ->get('track_enabled_source_entity_types');
    foreach (\Drupal::entityTypeManager()
      ->getDefinitions() as $entity_type_id => $entity_type) {

      // Only look for entities enabled for tracking on the settings form.
      $track_this_entity_type = FALSE;
      if (!is_array($to_track) && $entity_type
        ->entityClassImplements('\\Drupal\\Core\\Entity\\ContentEntityInterface')) {

        // When no settings are defined, track all content entities by default,
        // except for Files and Users.
        if (!in_array($entity_type_id, [
          'file',
          'user',
        ])) {
          $track_this_entity_type = TRUE;
        }
      }
      elseif (is_array($to_track) && in_array($entity_type_id, $to_track, TRUE)) {
        $track_this_entity_type = TRUE;
      }
      if ($track_this_entity_type) {

        // Delete current usage statistics for these entities.
        \Drupal::service('entity_usage.usage')
          ->bulkDeleteSources($entity_type_id);

        // Add all existing ids to be tracked again.
        $ids = \Drupal::entityQuery($entity_type_id)
          ->accessCheck(FALSE)
          ->execute();
        if (!empty($ids)) {
          $sandbox['total'] += count($ids);
          foreach ($ids as $id) {
            $sandbox['entities'][] = [
              'entity_type' => $entity_type_id,
              'entity_id' => $id,
            ];
          }
        }
      }
    }
  }

  // Abort the batch process if the site is big enough for this process to be
  // a very long-running process.
  $limit = Settings::get('entity_usage_2x_regenerate_limit', 2000);
  if ($sandbox['total'] > $limit) {
    $sandbox = [];
    return t('The automatic regeneration of usage statistics was skipped because it could be potentially slow on this site. Make sure you visit the <a href="@batch_url">batch update</a> page and trigger the update manually.', [
      '@batch_url' => Url::fromRoute('entity_usage.batch_update')
        ->toString(),
    ]);
  }

  // Worker.
  $batch_size = 1;
  for ($i = $sandbox['current_key']; $i < $sandbox['current_key'] + $batch_size; $i++) {
    if (empty($sandbox['entities'][$i])) {
      break;
    }
    $entity_type = $sandbox['entities'][$i]['entity_type'];
    $entity_id = $sandbox['entities'][$i]['entity_id'];
    if ($entity_type && $entity_id) {
      $entity_storage = \Drupal::entityTypeManager()
        ->getStorage($entity_type);

      /** @var \Drupal\Core\Entity\EntityInterface $entity */
      $entity = $entity_storage
        ->load($entity_id);
      if ($entity
        ->getEntityType()
        ->isRevisionable()) {

        // Track all revisions and translations of the source entity. Sources
        // are tracked as if they were new entities.
        $result = $entity_storage
          ->getQuery()
          ->allRevisions()
          ->condition($entity
          ->getEntityType()
          ->getKey('id'), $entity
          ->id())
          ->sort($entity
          ->getEntityType()
          ->getKey('revision'), 'DESC')
          ->execute();
        $revision_ids = array_keys($result);
        foreach ($revision_ids as $revision_id) {

          /** @var \Drupal\Core\Entity\EntityInterface $entity_revision */
          if (!($entity_revision = $entity_storage
            ->loadRevision($revision_id))) {
            continue;
          }
          \Drupal::service('entity_usage.entity_update_manager')
            ->trackUpdateOnCreation($entity_revision);
        }
      }
      else {

        // Sources are tracked as if they were new entities.
        \Drupal::service('entity_usage.entity_update_manager')
          ->trackUpdateOnCreation($entity);
      }
    }
    $sandbox['current_key']++;
  }
  $sandbox['#finished'] = empty($sandbox['total']) ? 1 : $sandbox['current_key'] / $sandbox['total'];
  return t('Finished generating statistics for @total_count entities.', [
    '@total_count' => $sandbox['total'],
  ]);
}

/**
 * Include "target_id_string" also as primary key in schema.
 */
function entity_usage_update_8203(&$sandbox) {

  // Left empty on purpose.
}

/**
 * Add source entity index to the entity_usage table.
 */
function entity_usage_update_8204(&$sandbox) {

  // This is deliberately duplicated, instead of calling hook_schema() to
  // obtain it.
  $spec = [
    'description' => 'Track entities that reference other entities.',
    'fields' => [
      'target_id' => [
        'description' => 'The target entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'target_id_string' => [
        'description' => 'The target ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'target_type' => [
        'description' => 'The target entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_id' => [
        'description' => 'The source entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'source_id_string' => [
        'description' => 'The source ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => FALSE,
      ],
      'source_type' => [
        'description' => 'The source entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_langcode' => [
        'description' => 'The source entity language code.',
        'type' => 'varchar_ascii',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_vid' => [
        'description' => 'The source entity revision ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'method' => [
        'description' => 'The method used to track the target, generally the plugin ID.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'field_name' => [
        'description' => 'The field in the source entity containing the target entity.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'count' => [
        'description' => 'The number of times the target entity is referenced in this case.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
    ],
    'primary key' => [
      'target_id',
      'target_id_string',
      'target_type',
      'source_id',
      'source_type',
      'source_langcode',
      'source_vid',
      'method',
      'field_name',
    ],
    'indexes' => [
      'target_entity' => [
        'target_type',
        'target_id',
      ],
      'target_entity_string' => [
        'target_type',
        'target_id_string',
      ],
      'source_entity' => [
        'source_type',
        'source_id',
      ],
      'source_entity_string' => [
        'source_type',
        'source_id_string',
      ],
    ],
  ];
  $schema = \Drupal::database()
    ->schema();
  if (!$schema
    ->indexExists('entity_usage', 'source_entity')) {
    $schema
      ->addIndex('entity_usage', 'source_entity', [
      'source_type',
      'source_id',
    ], $spec);
  }
  if (!$schema
    ->indexExists('entity_usage', 'source_entity_string')) {
    $schema
      ->addIndex('entity_usage', 'source_entity_string', [
      'source_type',
      'source_id_string',
    ], $spec);
  }
}

/**
 * Initialize the new "usage_controller_items_per_page" config value to 25.
 */
function entity_usage_update_8205(&$sandbox) {
  $config = \Drupal::configFactory()
    ->getEditable('entity_usage.settings');
  $items_per_page = $config
    ->get('usage_controller_items_per_page');
  if (empty($items_per_page)) {
    $config
      ->set('usage_controller_items_per_page', ListUsageController::ITEMS_PER_PAGE_DEFAULT)
      ->save(TRUE);
  }
}

/**
 * Rename items_per_page into items_per_group.
 */
function entity_usage_update_8300(&$sandbox) {
  $config = \Drupal::configFactory()
    ->getEditable('entity_usage.settings');
  $previous_setting = $config
    ->get('usage_controller_items_per_page');
  if (!empty($previous_setting)) {
    $config
      ->set('usage_controller_items_per_page', NULL)
      ->set('usage_controller_items_per_group', $previous_setting)
      ->save(TRUE);
  }
}

/**
 * Recreate the entity usage table with the new schema.
 */
function entity_usage_update_8301(&$sandbox) {
  $schema = \Drupal::database()
    ->schema();
  $schema
    ->dropTable('entity_usage');

  // This is deliberately duplicated instead of calling entity_usage_schema().
  $new_spec = [
    'description' => 'Track entities that reference other entities.',
    'fields' => [
      'target_id' => [
        'description' => 'The target entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'target_id_string' => [
        'description' => 'The target ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'target_type' => [
        'description' => 'The target entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_id' => [
        'description' => 'The source entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
      'source_id_string' => [
        'description' => 'The source ID, when the entity uses string IDs.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => FALSE,
      ],
      'source_type' => [
        'description' => 'The source entity type.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_langcode' => [
        'description' => 'The source entity language code.',
        'type' => 'varchar_ascii',
        'length' => 12,
        'not null' => TRUE,
        'default' => '',
      ],
      'source_vid' => [
        'description' => 'The source entity revision ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
      ],
    ],
    'primary key' => [
      'target_id',
      'target_id_string',
      'target_type',
      'source_id',
      'source_type',
      'source_langcode',
      'source_vid',
    ],
    'indexes' => [
      'target_entity' => [
        'target_type',
        'target_id',
      ],
      'target_entity_string' => [
        'target_type',
        'target_id_string',
      ],
      'source_entity' => [
        'source_type',
        'source_id',
      ],
      'source_entity_string' => [
        'source_type',
        'source_id_string',
      ],
    ],
  ];
  $schema
    ->createTable('entity_usage', $new_spec);
}

Functions

Namesort descending Description
entity_usage_install Implements hook_install().
entity_usage_post_update_regenerate_2x Re-generate entity_usage statistics.
entity_usage_requirements Implements hook_requirements().
entity_usage_schema Implements hook_schema().
entity_usage_update_8001 Include "method" also as primary key for the {entity_usage} table.
entity_usage_update_8201 Recreate the entity usage table with the new schema.
entity_usage_update_8202 Trigger entity usage statistics in the new schema.
entity_usage_update_8203 Include "target_id_string" also as primary key in schema.
entity_usage_update_8204 Add source entity index to the entity_usage table.
entity_usage_update_8205 Initialize the new "usage_controller_items_per_page" config value to 25.
entity_usage_update_8300 Rename items_per_page into items_per_group.
entity_usage_update_8301 Recreate the entity usage table with the new schema.