Skip to content

Instantly share code, notes, and snippets.

@drubb
Created February 9, 2025 17:07
Show Gist options
  • Save drubb/4430370f91cf329f184fdebca141dcf2 to your computer and use it in GitHub Desktop.
Save drubb/4430370f91cf329f184fdebca141dcf2 to your computer and use it in GitHub Desktop.
Drush script to update changed field definitions for a price field, switched from integer to decimal. The script is run after the related code changes have been deployed, it updates the underlying database structures.
<?php declare(strict_types=1);
/**
* @file
* Drush script to change the price fields from integer to decimal.
*/
use Drupal\Core\Database\Database;
// Check whether there are changes to field definitions.
$change_list = \Drupal::entityDefinitionUpdateManager()->getChangeSummary();
if (empty($change_list)) {
\Drupal::messenger()
->addMessage(t('There are no changes to field definitions.'));
return;
}
// Backup and clear the related tables.
$tables = ['node__field_prices', 'node_revision__field_prices'];
foreach ($tables as $table) {
// Copy the table to a backup table.
copy_table($table, $table . '_backup');
// Truncate the original table.
truncate_table($table);
}
// Make the changes to the field definitions. There's just one field.
$field_definition = \Drupal::entityDefinitionUpdateManager()
->getFieldStorageDefinition('field_prices', 'node');
\Drupal::entityDefinitionUpdateManager()
->updateFieldStorageDefinition($field_definition);
// Restore the data and drop the backup tables.
foreach ($tables as $table) {
restore_table($table . '_backup', $table);
drop_table($table . '_backup');
}
/**
* Function to duplicate a table in the database.
*
* @param string $existing_table_name
* The name of the existing table.
* @param string $new_table_name
* The name of the new table.
*/
function copy_table(string $existing_table_name, string $new_table_name): void {
// Get the database connection.
$connection = Database::getConnection();
// Build the query to copy the table structure and data.
$query = "CREATE TABLE $new_table_name AS SELECT * FROM $existing_table_name";
try {
// Execute the query.
$connection->query($query);
\Drupal::messenger()
->addMessage(t('Table @new_table_name has been created successfully.', ['@new_table_name' => $new_table_name]));
}
catch (\Exception $e) {
\Drupal::messenger()
->addError(t('Failed to create table: @message', ['@message' => $e->getMessage()]));
exit(1);
}
}
/**
* Function to truncate a table in the database.
*
* @param string $table_name
* The name of the table to truncate.
*/
function truncate_table(string $table_name): void {
// Get the database connection.
$connection = Database::getConnection();
// Build the query to truncate the table.
$query = "TRUNCATE TABLE $table_name";
try {
// Execute the query.
$connection->query($query);
\Drupal::messenger()
->addMessage(t('Table @table_name has been truncated successfully.', ['@table_name' => $table_name]));
}
catch (\Exception $e) {
\Drupal::messenger()
->addError(t('Failed to truncate table: @message', ['@message' => $e->getMessage()]));
exit(1);
}
}
/**
* Function to restore data from a backup table to the original table.
*
* @param string $backup_table_name
* The name of the backup table.
* @param string $original_table_name
* The name of the original table.
*/
function restore_table(string $backup_table_name, string $original_table_name): void {
// Get the database connection.
$connection = Database::getConnection();
// Build the query to restore the data from the backup table.
$query = "INSERT INTO $original_table_name SELECT * FROM $backup_table_name";
try {
// Execute the query.
$connection->query($query);
\Drupal::messenger()
->addMessage(t('Data has been restored from @backup_table_name to @original_table_name.', [
'@backup_table_name' => $backup_table_name,
'@original_table_name' => $original_table_name,
]));
}
catch (\Exception $e) {
\Drupal::messenger()
->addError(t('Failed to restore data: @message', ['@message' => $e->getMessage()]));
exit(1);
}
}
/**
* Function to drop a table in the database.
*
* @param string $table_name
* The name of the table to drop.
*/
function drop_table(string $table_name): void {
// Get the database connection.
$connection = Database::getConnection();
// Build the query to drop the table.
$query = "DROP TABLE $table_name";
try {
// Execute the query.
$connection->query($query);
\Drupal::messenger()
->addMessage(t('Table @table_name has been dropped successfully.', ['@table_name' => $table_name]));
}
catch (\Exception $e) {
\Drupal::messenger()
->addError(t('Failed to drop table: @message', ['@message' => $e->getMessage()]));
exit(1);
}
}
@drubb
Copy link
Author

drubb commented Feb 19, 2025

This Gist is meant for fields defined programmatically, where the necessary changes have been made in code already.
To change the field settings for a field defined in the UI, the schema change must be run explicitly. Example:

$database = Database::getConnection();
if ($database->schema()
  ->fieldExists('node__field_prices', 'field_prices_value')) {
  $database->schema()
    ->changeField('node__field_prices', 'field_prices_value', 'field_prices_value', [
      'type' => 'numeric',
      'precision' => 10,
      'scale' => 2,
      'unsigned' => TRUE,
    ]);
  $database->schema()
    ->changeField('node_revision__field_prices', 'field_prices_value', 'field_prices_value', [
      'type' => 'numeric',
      'precision' => 10,
      'scale' => 2,
      'unsigned' => TRUE,
    ]);
}

Additionally, it might be necessary to adjust any keys using this field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment