Skip to content

Instantly share code, notes, and snippets.

@steffenr
Last active July 3, 2026 10:23
Show Gist options
  • Select an option

  • Save steffenr/e5115c4fe4e4c69fb076616edb07cb13 to your computer and use it in GitHub Desktop.

Select an option

Save steffenr/e5115c4fe4e4c69fb076616edb07cb13 to your computer and use it in GitHub Desktop.
Updating text field length with existing data in Drupal 11
<?php
/**
* @file
* Post update functions for my_module module.
*/
declare(strict_types=1);
use Drupal\Core\Database\Database;
use Drupal\field\FieldStorageConfigInterface;
/**
* Increase the maximum length of paragraph subheadline field to 512 characters.
*/
function my_module_post_update_increase_max_subheadline_length(array &$sandbox): void {
$entity_type = 'paragraph';
$field_name = 'field_subheadline_text';
$new_length = 512;
$value_column = "{$field_name}_value";
$tables = [
"{$entity_type}__{$field_name}",
"{$entity_type}_revision__{$field_name}",
];
// 1. Alter the DB columns directly. Drupal's entity API refuses to change a
// field's schema once it holds data
// (SqlContentEntityStorageSchema::onFieldStorageDefinitionUpdate() throws
// FieldStorageDefinitionUpdateForbiddenException), so we widen the columns
// ourselves and then sync the tracked definitions below. Widening a varchar
// never truncates existing values.
$db_schema = Database::getConnection()->schema();
$column_spec = ['type' => 'varchar', 'length' => $new_length, 'not null' => FALSE];
foreach ($tables as $table) {
if ($db_schema->tableExists($table)) {
$db_schema->changeField($table, $value_column, $value_column, $column_spec);
}
}
// 2. Sync the installed SQL storage schema so the entity definition update
// manager stops reporting a mismatch. This is stored in the
// "entity.storage_schema.sql" key-value collection under the key
// "{entity_type}.field_schema_data.{field_name}" (see
// SqlContentEntityStorageSchema::loadFieldSchemaData()).
$kv = \Drupal::keyValue('entity.storage_schema.sql');
$schema_key = "{$entity_type}.field_schema_data.{$field_name}";
$field_schema = $kv->get($schema_key);
if (is_array($field_schema)) {
foreach ($field_schema as &$table_schema) {
if (is_array($table_schema)
&& is_array($table_schema['fields'] ?? NULL)
&& is_array($table_schema['fields'][$value_column] ?? NULL)
&& array_key_exists('length', $table_schema['fields'][$value_column])
) {
$table_schema['fields'][$value_column]['length'] = $new_length;
}
}
$kv->set($schema_key, $field_schema);
}
// 3. Update the active field storage config. Use the raw config factory to
// bypass the entity API (a FieldStorageConfig::save() would re-trigger the
// forbidden-schema-change check from step 1).
\Drupal::configFactory()
->getEditable("field.storage.{$entity_type}.{$field_name}")
->set('settings.max_length', $new_length)
->save();
// 4. Update the last-installed field storage definition so it matches the
// active config.
$repository = \Drupal::service('entity.last_installed_schema.repository');
$definitions = $repository->getLastInstalledFieldStorageDefinitions($entity_type);
if (isset($definitions[$field_name]) && $definitions[$field_name] instanceof FieldStorageConfigInterface) {
$definitions[$field_name]->setSetting('max_length', $new_length);
$repository->setLastInstalledFieldStorageDefinitions($entity_type, $definitions);
}
// 5. Clear cached definitions so the new schema takes effect immediately.
\Drupal::service('entity_type.manager')->clearCachedDefinitions();
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment