Commit 8fabb319 authored by Steffen Schüssler's avatar Steffen Schüssler
Browse files

fixed update hook for increasing varchar length of width and height...

fixed update hook for increasing varchar length of width and height properties, Fixes: Issue #3285097 by neffets: allow vh and wh units in width and height values, limited by 4 characters
parent 01a9f614
Loading
Loading
Loading
Loading
+10 −56
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 */

use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\iframe\FieldTypeUpdateUtil;

/**
 * Implements hook_install().
@@ -130,67 +131,20 @@ function iframe_update_8201(&$sandbox) {
}

/**
 * increase fields width and heighti to varchar(7), supersedes update number 8202
 * dummy for not working update-hook
 */
function iframe_update_8203(&$sandbox) {
}

/**
 * increase fields width and height to varchar(7), supersedes update number 8202
 */
function iframe_update_8204(&$sandbox) {
  // Caches have to be cleared first to ensure new fields are detected in the
  // code.
  drupal_flush_all_caches();

  /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager */
  $entityFieldManager = \Drupal::service('entity_field.manager');
  $entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
  $entityTypeManager = \Drupal::entityTypeManager();

  $iframeFieldMap = $entityFieldManager->getFieldMapByFieldType('iframe');
  $schema = \Drupal::database()->schema();

  // Loop through the array of iframe fields keyed by entity type...
  foreach ($iframeFieldMap as $entityTypeId => $fields) {
    foreach (array_keys($fields) as $fieldName) {
      \Drupal::messenger()->addMessage('Transform field: ' . $fieldName);
      $fieldStorageDefinition = $entityDefinitionUpdateManager->getFieldStorageDefinition($fieldName, $entityTypeId);

      // ... if the field is in a ContentEntity stored in SQL...
      $storage = $entityTypeManager->getStorage($entityTypeId);
      if ($storage instanceof SqlContentEntityStorage) {
        // ... get a map of field columns to SQL columns for that field.
        $tableMapping = $storage->getTableMapping([
          $fieldName => $fieldStorageDefinition,
        ]);

        $tableNames = $tableMapping->getDedicatedTableNames();
        $columns = $tableMapping->getColumnNames($fieldName);
        \Drupal::messenger()->addMessage('  ==> in tables: ' . implode(", ", $tableNames));

        // For each table (e.g.: data, revision), check whether the
        // 'allowfullscreen' column exists. If it does not, create it.
        foreach ($tableNames as $tableName) {
          $field_schema = $fieldStorageDefinition->getSchema();

          $fieldExists = $schema->fieldExists($tableName, $columns['width']);
          $tableExists = $schema->tableExists($tableName);

          if ($fieldExists && $tableExists) {
            $schema->changeField($tableName, $columns['width'], $columns['width'], $field_schema['columns']['width']);
            $schema->changeField($tableName, $columns['height'], $columns['height'], $field_schema['columns']['height']);
          }
        }

        // Make sure the field storage definition is updated.
        // ** normally claims changes-only-on-nodata,
        // ** Here we KNOW for sure, that the field will become larger so the data fits
        #emulate $entityDefinitionUpdateManager->updateFieldStorageDefinition($fieldStorageDefinition);
        // ignore original (old size=4 for width and height, new is greater with 7, its safe)
        $service = \Drupal::service('entity.last_installed_schema.repository');
        try {
          $service->setLastInstalledFieldStorageDefinition($fieldStorageDefinition);
        } catch (Exception $e) {
          \Drupal::messenger()->addMessage(print_r([$e->getCode(), substr($e->getMessage(), 0, 512)], true));
          \Drupal::messenger()->addMessage('ACHTUNG: If an update-error occured, please run update.php a second time.');
        }
      }
    }
  }
  $field_type = 'iframe';
  FieldTypeUpdateUtil::_field_type_schema_column_spec_change_helper($field_type);
}
+289 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\iframe;

use Drupal;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;

class FieldTypeUpdateUtil {

  /**
   * Helper function for HOOK_Update to update the field schema columns.
   *
   * Based on address.install (thanks to the maintainer!)
   *
   * @param $field_type The field type id.
   * @param array $columns_to_add array of the column names from schema() function.
   */
  public static function _field_type_schema_column_add_helper($field_type, array $columns_to_add = array()) {
    $processed_fields = [];
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
    $field_definition = $field_type_manager->getDefinition($field_type);
    $field_item_class = $field_definition['class'];
  
    $schema = \Drupal::database()->schema();
    $entity_type_manager = \Drupal::entityTypeManager();
    $entity_field_manager = \Drupal::service('entity_field.manager');
    $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type);
    // The key-value collection for tracking installed storage schema.
    $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql');
    $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed');
  
    foreach ($entity_field_map as $entity_type_id => $field_map) {
      $entity_storage = $entity_type_manager->getStorage($entity_type_id);
  
      // Only SQL storage based entities are supported / throw known exception.
      //    if (!($entity_storage instanceof SqlContentEntityStorage)) {
      //      continue;
      //    }
  
      $entity_type = $entity_type_manager->getDefinition($entity_type_id);
      $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id);
      /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
      $table_mapping = $entity_storage->getTableMapping($field_storage_definitions);
      // Only need field storage definitions of our field type:
      /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */
      foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) {
        $field_name = $field_storage_definition->getName();
        try {
          $table = $table_mapping->getFieldTableName($field_name);
        } catch (SqlContentEntityStorageException $e) {
          // Custom storage? Broken site? No matter what, if there is no table
          // or column, there's little we can do.
          continue;
        }
        // See if the field has a revision table.
        $revision_table = NULL;
        if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) {
          if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
            $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
          }
          elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
            $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
          }
        }
        // Load the installed field schema so that it can be updated.
        $schema_key = "$entity_type_id.field_schema_data.$field_name";
        $field_schema_data = $entity_storage_schema_sql->get($schema_key);
  
        $processed_fields[] = [$entity_type_id, $field_name];
        // Loop over each new column and add it as a schema column change.
        foreach ($columns_to_add as $column_id) {
          $column = $table_mapping->getFieldColumnName($field_storage_definition, $column_id);
          // Add `initial_from_field` to the new spec, as this will copy over
          // the entire data.
          $field_schema = $field_item_class::schema($field_storage_definition);
          $spec = $field_schema['columns'][$column_id];
  
          // Add the new column.
          $schema->addField($table, $column, $spec);
          if ($revision_table) {
            $schema->addField($revision_table, $column, $spec);
          }
  
          // Add the new column to the installed field schema.
          if (!empty($field_schema_data)) {
            $field_schema_data[$table]['fields'][$column] = $field_schema['columns'][$column_id];
            $field_schema_data[$table]['fields'][$column]['not null'] = FALSE;
            if ($revision_table) {
              $field_schema_data[$revision_table]['fields'][$column] = $field_schema['columns'][$column_id];
              $field_schema_data[$revision_table]['fields'][$column]['not null'] = FALSE;
            }
          }
        }
  
        // Save changes to the installed field schema.
        if (!empty($field_schema_data)) {
          $entity_storage_schema_sql->set($schema_key, $field_schema_data);
        }
        if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
          $key = "$entity_type_id.field_storage_definitions";
          if ($definitions = $entity_definitions_installed->get($key)) {
            $definitions[$field_name] = $field_storage_definition;
            $entity_definitions_installed->set($key, $definitions);
          }
        }
      }
    }
  }
  
  /**
   * Helper function for HOOK_Update to update the field schema to current, preserving existing data
   *
   * @param $field_type The field type id e.g. "iframe"
   * @param array $column
   */
  public static function _field_type_schema_column_spec_change_helper($field_type) {
    $processed_fields = [];
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
    $field_definition = $field_type_manager->getDefinition($field_type);
    $field_item_class = $field_definition['class'];
  
    $schema = \Drupal::database()->schema();
    $entity_type_manager = \Drupal::entityTypeManager();
    $entity_field_manager = \Drupal::service('entity_field.manager');
    $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type);
    // The key-value collection for tracking installed storage schema.
    $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql');
    $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed');
  
    foreach ($entity_field_map as $entity_type_id => $field_map) {
      $entity_storage = $entity_type_manager->getStorage($entity_type_id);
      $entity_type = $entity_type_manager->getDefinition($entity_type_id);
      $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id);
      /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
      $table_mapping = $entity_storage->getTableMapping($field_storage_definitions);
      // Only need field storage definitions of our field type:
      /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */
      foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) {
        $field_name = $field_storage_definition->getName();
        $tables = [];
        try {
          $table = $table_mapping->getFieldTableName($field_name);
          $tables[] = $table;
        } catch (SqlContentEntityStorageException $e) {
          // Custom storage? Broken site? No matter what, if there is no table
          // there's little we can do.
          continue;
        }
        // See if the field has a revision table.
        $revision_table = NULL;
        if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) {
          if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
            $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
            $tables[] = $revision_table;
          }
          elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
            $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
            $tables[] = $revision_table;
          }
        }
  
        $database = \Drupal::database();
        $existing_data = [];
        foreach ($tables as $table) {
          // Get the old data.
          $existing_data[$table] = $database->select($table)
            ->fields($table)
            ->execute()
            ->fetchAll(\PDO::FETCH_ASSOC);
  
          // Wipe it.
          $database->truncate($table)->execute();
        }
  
        $manager = \Drupal::entityDefinitionUpdateManager();
        $manager->updateFieldStorageDefinition($manager->getFieldStorageDefinition($field_name, $entity_type_id));
  
  
        // Restore the data.
        foreach ($tables as $table) {
          $insert_query = $database
            ->insert($table)
            ->fields(array_keys(end($existing_data[$table])));
          foreach ($existing_data[$table] as $row) {
            $insert_query->values(array_values($row));
          }
          $insert_query->execute();
        }
      }
    }
  }
  
  /**
   * Helper function for HOOK_Update to remove columns from the field schema.
   *
   * @param $field_type The field type id e.g. "drowl_paragraphs_settings"
   * @param array $columns_to_remove array of the column names from schema() function, e.g. ["style_textalign"]
   */
  public static function _field_type_schema_column_remove_helper($field_type, array $columns_to_remove = array()) {
    $processed_fields = [];
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
    $field_definition = $field_type_manager->getDefinition($field_type);
    $field_item_class = $field_definition['class'];
  
    $schema = \Drupal::database()->schema();
    $entity_type_manager = \Drupal::entityTypeManager();
    $entity_field_manager = \Drupal::service('entity_field.manager');
    $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type);
    // The key-value collection for tracking installed storage schema.
    $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql');
    $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed');
  
    foreach ($entity_field_map as $entity_type_id => $field_map) {
      $entity_storage = $entity_type_manager->getStorage($entity_type_id);
  
      // Only SQL storage based entities are supported / throw known exception.
      //    if (!($entity_storage instanceof SqlContentEntityStorage)) {
      //      continue;
      //    }
  
      $entity_type = $entity_type_manager->getDefinition($entity_type_id);
      $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id);
      /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
      $table_mapping = $entity_storage->getTableMapping($field_storage_definitions);
      // Only need field storage definitions of our field type:
      /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */
      foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) {
        $field_name = $field_storage_definition->getName();
        try {
          $table = $table_mapping->getFieldTableName($field_name);
        } catch (SqlContentEntityStorageException $e) {
          // Custom storage? Broken site? No matter what, if there is no table
          // or column, there's little we can do.
          continue;
        }
        // See if the field has a revision table.
        $revision_table = NULL;
        if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) {
          if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
            $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
          }
          elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
            $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
          }
        }
        // Load the installed field schema so that it can be updated.
        $schema_key = "$entity_type_id.field_schema_data.$field_name";
        $field_schema_data = $entity_storage_schema_sql->get($schema_key);
  
        $processed_fields[] = [$entity_type_id, $field_name];
        // Loop over each new column and add it as a schema column change.
        foreach ($columns_to_remove as $column_id) {
          $column = $table_mapping->getFieldColumnName($field_storage_definition, $column_id);
          // Add `initial_from_field` to the new spec, as this will copy over
          // the entire data.
          $field_schema = $field_item_class::schema($field_storage_definition);
          $spec = $field_schema['columns'][$column_id];
  
          // Add the new column.
          $schema->dropField($table, $column);
          if ($revision_table) {
            $schema->dropField($revision_table, $column);
          }
  
          // Remove the column from the installed field schema.
          if (!empty($field_schema_data)) {
            unset($field_schema_data[$table]['fields'][$column]);
            if ($revision_table) {
              unset($field_schema_data[$revision_table]['fields'][$column]);
            }
          }
        }
  
        // Save changes to the installed field schema.
        if (!empty($field_schema_data)) {
          $entity_storage_schema_sql->set($schema_key, $field_schema_data);
        }
        if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
          $key = "$entity_type_id.field_storage_definitions";
          if ($definitions = $entity_definitions_installed->get($key)) {
            $definitions[$field_name] = $field_storage_definition;
            $entity_definitions_installed->set($key, $definitions);
          }
        }
      }
    }
  }

}