You are here

public function MultiversionStorageSchemaConverter::convertToMultiversionable in Multiversion 8.2

File

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

Class

MultiversionStorageSchemaConverter

Namespace

Drupal\multiversion\Entity\Storage\Sql

Code

public function convertToMultiversionable(array &$sandbox) {

  // Return if the conversion for current entity type has been finished.
  if (isset($sandbox[$this->entityTypeId]['finished']) && $sandbox[$this->entityTypeId]['finished'] == 1 || !empty($sandbox[$this->entityTypeId]['failed'])) {
    return;
  }

  // Initialize entity types conversion.
  $this
    ->initializeConversion($sandbox);

  // If the condition is TRUE, then this will be the first run of the
  // operation.
  if (!isset($sandbox[$this->entityTypeId]['finished']) || $sandbox[$this->entityTypeId]['finished'] < 1) {

    // Store the original entity type and field definitions in the $sandbox
    // array so we can use them later in the update process.
    $this
      ->collectOriginalDefinitions($sandbox);

    // Create a temporary environment in which the new data will be stored.
    $fields_to_update = $this
      ->getFieldsToUpdate();
    $this
      ->createTemporaryDefinitions($sandbox, $fields_to_update);

    // Create the updated entity schema using temporary tables.

    /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
    $storage = $this->entityTypeManager
      ->getStorage($this->entityTypeId);
    $storage
      ->setTemporary(TRUE);
    $storage
      ->setEntityType($sandbox['temporary_entity_type']);
    $storage
      ->onEntityTypeCreate($sandbox['temporary_entity_type']);
  }

  // Copy over the existing data to the new temporary tables.
  $this
    ->copyData($sandbox);

  // If the data copying has finished successfully, we can drop the temporary
  // tables and call the appropriate update mechanisms.
  if ($sandbox[$this->entityTypeId]['finished'] == 1) {
    $sandbox['current_id'] = 0;
    $this->entityTypeManager
      ->useCaches(FALSE);
    $actual_entity_type = $this->entityTypeManager
      ->getDefinition($this->entityTypeId);

    // Rename the original tables so we can put them back in place in case
    // anything goes wrong.
    $backup_table_names = array_combine($sandbox['original_table_mapping']
      ->getTableNames(), $sandbox['backup_table_mapping']
      ->getTableNames());
    foreach ($backup_table_names as $original_table_name => $backup_table_name) {
      $this->database
        ->schema()
        ->renameTable($original_table_name, $backup_table_name);
    }

    // Put the new tables in place and update the entity type and field
    // storage definitions.
    try {
      $storage = $this->entityTypeManager
        ->createHandlerInstance($actual_entity_type
        ->getStorageClass(), $actual_entity_type);
      $current_table_mapping = $storage
        ->getCustomTableMapping($actual_entity_type, $sandbox['updated_storage_definitions']);
      $table_name_mapping = array_combine($sandbox['temporary_table_mapping']
        ->getTableNames(), $current_table_mapping
        ->getTableNames());
      foreach ($table_name_mapping as $temp_table_name => $new_table_name) {
        $this->database
          ->schema()
          ->renameTable($temp_table_name, $new_table_name);
      }

      // Rename the tables in the cached entity schema data.
      $entity_schema_data = $this->installedStorageSchema
        ->get($this->entityTypeId . '.entity_schema_data', []);
      foreach ($entity_schema_data as $temp_table_name => $schema) {
        if (isset($table_name_mapping[$temp_table_name])) {
          $entity_schema_data[$table_name_mapping[$temp_table_name]] = $schema;
          unset($entity_schema_data[$temp_table_name]);
        }
      }
      $this->installedStorageSchema
        ->set($this->entityTypeId . '.entity_schema_data', $entity_schema_data);

      // Rename the tables in the cached field schema data.
      foreach ($sandbox['updated_storage_definitions'] as $storage_definition) {
        $field_schema_data = $this->installedStorageSchema
          ->get($this->entityTypeId . '.field_schema_data.' . $storage_definition
          ->getName(), []);
        foreach ($field_schema_data as $temp_table_name => $schema) {
          if (isset($table_name_mapping[$temp_table_name])) {
            $field_schema_data[$table_name_mapping[$temp_table_name]] = $schema;
            unset($field_schema_data[$temp_table_name]);
          }
        }
        $this->installedStorageSchema
          ->set($this->entityTypeId . '.field_schema_data.' . $storage_definition
          ->getName(), $field_schema_data);
      }

      // Instruct the entity schema handler that data migration has been
      // handled already and update the entity type.
      $actual_entity_type
        ->set('requires_data_migration', FALSE);
      $this->entityDefinitionUpdateManager
        ->updateEntityType($actual_entity_type);

      // Update the field storage definitions.
      $this
        ->updateFieldStorageDefinitionsToRevisionable($actual_entity_type, $sandbox['original_storage_definitions'], $fields_to_update);

      // Install the published status field.
      $this
        ->installPublishedStatusField($actual_entity_type);

      // Install the fields provided by Multiversion.
      $this
        ->installMultiversionFields($actual_entity_type);

      // Multiversion makes changes for other fields, those updates need to be
      // applied.
      $this->entityTypeManager
        ->clearCachedDefinitions();
      $field_definitions = $this->entityFieldManager
        ->getFieldStorageDefinitions($this->entityTypeId);
      foreach ($field_definitions as $field_definition) {
        $this->entityDefinitionUpdateManager
          ->updateFieldStorageDefinition($field_definition);
      }
    } catch (\Exception $e) {

      // Something went wrong, bring back the original tables.
      foreach ($backup_table_names as $original_table_name => $backup_table_name) {

        // We are in the 'original data recovery' phase, so we need to be sure
        // that the initial tables can be properly restored.
        if ($this->database
          ->schema()
          ->tableExists($original_table_name)) {
          $this->database
            ->schema()
            ->dropTable($original_table_name);
        }
        $this->database
          ->schema()
          ->renameTable($backup_table_name, $original_table_name);
      }

      // Re-throw the original exception.
      throw $e;
    }

    // At this point the update process either finished successfully or any
    // error has been handled already, so we can drop the backup entity
    // tables.
    foreach ($backup_table_names as $original_table_name => $backup_table_name) {
      $this->database
        ->schema()
        ->dropTable($backup_table_name);
    }
  }
}