You are here

protected function MultiversionStorageSchemaConverter::copyData in Multiversion 8.2

1 call to MultiversionStorageSchemaConverter::copyData()
MultiversionStorageSchemaConverter::convertToMultiversionable in src/Entity/Storage/Sql/MultiversionStorageSchemaConverter.php

File

src/Entity/Storage/Sql/MultiversionStorageSchemaConverter.php, line 417

Class

MultiversionStorageSchemaConverter

Namespace

Drupal\multiversion\Entity\Storage\Sql

Code

protected function copyData(array &$sandbox) {

  /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $temporary_table_mapping */
  $temporary_table_mapping = $sandbox['temporary_table_mapping'];
  $temporary_entity_type = $sandbox['temporary_entity_type'];
  $original_table_mapping = $sandbox['original_table_mapping'];
  $original_entity_type = $sandbox['original_entity_type'];
  $original_base_table = $original_entity_type
    ->getBaseTable();
  $revision_id_key = $temporary_entity_type
    ->getKey('revision');
  $published_key = $temporary_entity_type
    ->getKey('published');
  $revision_default_key = $temporary_entity_type
    ->getRevisionMetadataKey('revision_default');
  $revision_translation_affected_key = $temporary_entity_type
    ->getKey('revision_translation_affected');
  if (!isset($sandbox['progress'])) {
    $sandbox['progress'] = 0;
  }
  if (!isset($sandbox[$this->entityTypeId]['progress'])) {
    $sandbox[$this->entityTypeId]['progress'] = 0;
  }
  $id = $original_entity_type
    ->getKey('id');

  // Get the next entity IDs to migrate.
  $entity_ids = $this->database
    ->select($original_base_table)
    ->fields($original_base_table, [
    $id,
  ])
    ->condition($id, $sandbox['current_id'], '>')
    ->orderBy($id, 'ASC')
    ->range(0, $sandbox['step_size'] ?: 50)
    ->execute()
    ->fetchAllKeyed(0, 0);

  /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
  $storage = $this->entityTypeManager
    ->getStorage($temporary_entity_type
    ->id());
  $storage
    ->setEntityType($original_entity_type);
  $storage
    ->setTableMapping($original_table_mapping);
  $entities = $storage
    ->loadMultiple($entity_ids);

  // Now inject the temporary entity type definition and table mapping in the
  // storage and re-save the entities.
  $storage
    ->setEntityType($temporary_entity_type);
  $storage
    ->setTableMapping($temporary_table_mapping);

  // This clear cache is needed at least for menu_link_content entity type.
  $this->entityTypeManager
    ->clearCachedDefinitions();
  foreach ($entities as $entity_id => $entity) {
    try {

      // Set the revision ID to be same as the entity ID.
      $entity
        ->set($revision_id_key, $entity_id);

      // We had no revisions so far, so the existing data belongs to the
      // default revision now.
      $entity
        ->set($revision_default_key, TRUE);

      // Set the published status to TRUE.
      $entity
        ->set($published_key, TRUE);

      // Set the revision token field.
      $rev_token = '1-' . md5($entity
        ->id() . $entity
        ->uuid() . $this->random
        ->string(10, TRUE));
      $entity
        ->set('_rev', $rev_token);
      $entity->_rev->new_edit = FALSE;

      // The _deleted field should be FALSE.
      $entity
        ->set('_deleted', FALSE);

      // Set the 'revision_translation_affected' flag to TRUE to match the
      // previous API return value: if the field was not defined the value
      // returned was always TRUE.
      if ($temporary_entity_type
        ->isTranslatable()) {
        $entity
          ->set($revision_translation_affected_key, TRUE);
      }

      // Treat the entity as new in order to make the storage do an INSERT
      // rather than an UPDATE.
      $entity
        ->enforceIsNew(TRUE);

      // Finally, save the entity in the temporary storage.
      $storage
        ->save($entity);

      // Delete the entry for the old entry in the menu_tree table.
      if ($original_entity_type
        ->id() == 'menu_link_content' && $this->database
        ->schema()
        ->tableExists('menu_tree')) {
        $this->database
          ->delete('menu_tree')
          ->condition('id', 'menu_link_content:' . $entity
          ->uuid())
          ->execute();
      }
    } catch (\Exception $e) {

      // In case of an error during the save process, we need to roll back the
      // original entity type and field storage definitions and clean up the
      // temporary tables.
      $this
        ->restoreOriginalDefinitions($sandbox);
      foreach ($temporary_table_mapping
        ->getTableNames() as $table_name) {
        $this->database
          ->schema()
          ->dropTable($table_name);
      }

      // Re-throw the original exception with a helpful message.
      throw new EntityStorageException("The entity update process failed while processing the entity {$original_entity_type->id()}:{$entity_id}.", $e
        ->getCode(), $e);
    }
    $sandbox['progress']++;
    $sandbox[$this->entityTypeId]['progress']++;
    $sandbox['current_id'] = $entity_id;
  }

  // If we're not in maintenance mode, the number of entities could change at
  // any time so make sure that we always use the latest record count.
  $max = 0;
  foreach ($sandbox['base_tables'] as $entity_type_id => $base_table) {
    $entities_count = $this->database
      ->select($sandbox['base_tables'][$entity_type_id])
      ->countQuery()
      ->execute()
      ->fetchField();
    $sandbox[$entity_type_id]['max'] = $entities_count;
    $max += $entities_count;
  }
  $sandbox['max'] = $max;
  $sandbox[$this->entityTypeId]['finished'] = empty($sandbox[$this->entityTypeId]['max']) ? 1 : $sandbox[$this->entityTypeId]['progress'] / $sandbox[$this->entityTypeId]['max'];
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['progress'] / $sandbox['max'];
}