You are here

protected function SqlContentEntityStorageSchema::updateSharedTableSchema in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::updateSharedTableSchema()

Updates the schema for a field stored in a shared table.

Parameters

\Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition: The storage definition of the field being updated.

\Drupal\Core\Field\FieldStorageDefinitionInterface $original: The original storage definition; i.e., the definition before the update.

Throws

\Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException Thrown when the update to the field is forbidden.

\Exception Rethrown exception if the table recreation fails.

File

core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php, line 1861

Class

SqlContentEntityStorageSchema
Defines a schema handler that supports revisionable, translatable entities.

Namespace

Drupal\Core\Entity\Sql

Code

protected function updateSharedTableSchema(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
  if (!$this->storage
    ->countFieldData($original, TRUE)) {
    if ($this->database
      ->supportsTransactionalDDL()) {

      // If the database supports transactional DDL, we can go ahead and rely
      // on it. If not, we will have to rollback manually if something fails.
      $transaction = $this->database
        ->startTransaction();
    }
    try {

      // Since there is no data we may be switching from a dedicated table
      // to a schema table schema, hence we should use the proper API.
      $this
        ->performFieldSchemaOperation('delete', $original);
      $this
        ->performFieldSchemaOperation('create', $storage_definition);
    } catch (\Exception $e) {
      if ($this->database
        ->supportsTransactionalDDL()) {
        $transaction
          ->rollBack();
      }
      else {

        // Recreate original schema.
        $this
          ->createSharedTableSchema($original);
      }
      throw $e;
    }
  }
  else {
    if ($this
      ->hasColumnChanges($storage_definition, $original)) {
      throw new FieldStorageDefinitionUpdateForbiddenException('The SQL storage cannot change the schema for an existing field (' . $storage_definition
        ->getName() . ' in ' . $storage_definition
        ->getTargetEntityTypeId() . ' entity) with data.');
    }
    $updated_field_name = $storage_definition
      ->getName();
    $table_mapping = $this
      ->getTableMapping($this->entityType, [
      $storage_definition,
    ]);
    $column_names = $table_mapping
      ->getColumnNames($updated_field_name);
    $schema_handler = $this->database
      ->schema();

    // Iterate over the mapped table to find the ones that host the deleted
    // field schema.
    $original_schema = $this
      ->loadFieldSchemaData($original);
    $schema = [];
    foreach ($table_mapping
      ->getTableNames() as $table_name) {
      foreach ($table_mapping
        ->getFieldNames($table_name) as $field_name) {
        if ($field_name == $updated_field_name) {
          $schema[$table_name] = $this
            ->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);

          // Handle NOT NULL constraints.
          foreach ($schema[$table_name]['fields'] as $column_name => $specifier) {
            $not_null = !empty($specifier['not null']);
            $original_not_null = !empty($original_schema[$table_name]['fields'][$column_name]['not null']);
            if ($not_null !== $original_not_null) {
              if ($not_null && $this
                ->hasNullFieldPropertyData($table_name, $column_name)) {
                throw new EntityStorageException("The {$column_name} column cannot have NOT NULL constraints as it holds NULL values.");
              }
              $column_schema = $original_schema[$table_name]['fields'][$column_name];
              $column_schema['not null'] = $not_null;
              $schema_handler
                ->changeField($table_name, $column_name, $column_name, $column_schema);
            }
          }

          // Drop original indexes and unique keys.
          if (!empty($original_schema[$table_name]['indexes'])) {
            foreach ($original_schema[$table_name]['indexes'] as $name => $specifier) {
              $schema_handler
                ->dropIndex($table_name, $name);
            }
          }
          if (!empty($original_schema[$table_name]['unique keys'])) {
            foreach ($original_schema[$table_name]['unique keys'] as $name => $specifier) {
              $schema_handler
                ->dropUniqueKey($table_name, $name);
            }
          }

          // Create new indexes and unique keys.
          if (!empty($schema[$table_name]['indexes'])) {
            foreach ($schema[$table_name]['indexes'] as $name => $specifier) {

              // Check if the index exists because it might already have been
              // created as part of the earlier entity type update event.
              $this
                ->addIndex($table_name, $name, $specifier, $schema[$table_name]);
            }
          }
          if (!empty($schema[$table_name]['unique keys'])) {
            foreach ($schema[$table_name]['unique keys'] as $name => $specifier) {
              $schema_handler
                ->addUniqueKey($table_name, $name, $specifier);
            }
          }

          // After deleting the field schema skip to the next table.
          break;
        }
      }
    }
    $this
      ->saveFieldSchemaData($storage_definition, $schema);
  }
}