Cambiar tipo de datos en campos de Drupal (Drupal 7)

Como realizar un cambio de tipo de dato en un campo de drupal. En este ejemplo veremos como cambiar campo de un número integer a un decimal.

Aún hoy, en el año 2025, hay sites que están construidos con la versión de Drupal 7. Estos sites quieren seguir funcionando y quizás una migración a versiones más modernas de Drupal sea un proceso bastante costoso que no se pueden permitir y necesitan de un mantenimiento o cambios para seguir funcionando.

El cambio del tipo en un campo en Drupal me ha parecido uno de los retos algo complejos que se pueden realizar, ya que requiere un elevado conocimiento del funcionamiento de este sistema. El sistema está diseñado para que no se permita de ninguna forma cambiar el tipo, ya que, aparentemente, no dispone de mecanismos para alterar los tipos de las columnas en la base de datos de forma automática y tiene sentido que sea así, ya que se puede producir una inconsistencia de datos como, por ejemplo, que se cambiase un varchar a un integer existiendo datos en la columna.

Se puede dar el caso, como el que nos ocupa ahora aquí, que necesitemos cambiar un integer a un decimal, ya que con el paso del tiempo podríamos necesitar una mayor precisión en un dato concreto para poder realizar algunos cálculos.

¿En qué necesitamos fijarnos a la hora de realizar este cambio?

Por un lado están las definiciones de las columnas en la base de datos a las que hay que cambiar el tipo de dato de integer a decimal con su precisión y desplazamiento. Por otro lado, la configuración del campo de Drupal y su visualización, que están en datos serializados almacenados en la base de datos y que tenemos que modificar para indicarle que ahora son un campo de tipo integer.

Lo primero que vamos a hacer es cambiar las definiciones de las columnas en la base de datos y para ello nos vamos a valer de la función db_change_field para cambiar tanto el field_data como el field_revision.

db_change_field('field_data_'.$fname, $fname.'_value', $fname.'_value', array(
    'type' => 'numeric',
    'precision' => 10,
    'scale' => 2,
    'not null' => FALSE, ));

db_change_field('field_revision_'.$fname, $fname.'_value', $fname.'_value', array(
    'type' => 'numeric',
    'precision' => 10,
    'scale' => 2,
    'not null' => FALSE,
));

Donde $fname es el nombre del campo. 

En el registro de la tabla field_config lo cambiamos a numero decimal:

db_query("UPDATE field_config SET type = 'number_decimal' WHERE field_name = '".$fname."'"); 

Obtenemos la configuración deserializada del campo y para ello nos valemos de la función field_info_field:

$field = field_info_field($fname);

Modificamos los dato de la información del campo para indicar el nuevo tipo, su precisión y escala:

$field['type'] = 'number_decimal';
$field['settings'] = [
    'precision' => 10,
    'scale' => 2,
    'decimal_separator' => '.',
];
$field['columns']['value'] = [
    'type' => 'numeric',
    'precision' => 10,
    'scale' => 2,
    'not null' => FALSE,
];

Obtenemos los datos de visualización del campo valiendonos de la funcion field_info_instance:

$instance_info = field_info_instance('<type>', $fname, '<bundle>');

Modificamos la información para indicar que se trata un decimal con precisión y escala:

$instance_info['display']['default']['type'] = 'number_decimal';
$instance_info['display']['full']['type'] = 'number_decimal';
$instance_info['display']['settings'] = [
    'precision' => 10,
    'scale' => 2,
    'decimal_separator' => '.',
];
$instance_info['display']['full']['settings'] = [
    'precision' => 10,
    'scale' => 2,
    'decimal_separator' => '.',
];

En este caso solo modificamos el display full que es el único que estamos usando. En el caso de usar otras visualizaciones se deberían añadir.

Actualizamos los cambios de visualización valiendonos de la función field_update_instance:

field_update_instance($instance_info)

Aún nos queda una tarea por hacer que es actualizar la configuración del campo en la base de datos. Lo normal sería poder usar la función field_udpate_field () pero en ningún caso he sido capaz de hacer que esta función actualize la información de la base de datos de forma correcta. En ese caso he optado por atacar la base de datos de forma directa con una static query:

$serilized_field = serialize($field);
db_query("update field_config set data= :data where field_name= :name", [':data' => $serilized_field, ':name' => $fname]);

De esta forma el campo quedará cambiado de tipo y funcionará sin problemas como si desde el principio lo hubieramos puesto como un campo decimal. 

En realidad no importa demasiado el orden en el que ejecutemos esta secuencia. Podemos cambiar la configuración antes y después las columnas en la base de datos o cambiar antes la información del display antes que la configuración del campo. Lo importante es que se ejecuten todas las partes sin errores. Haremos un hook_update_N para ejecutar toda la secuencia de código:

/**
 * change fields from type integer to decimal
 */
function hook_update_N() 
{
    $fnames = ['field_integer_one', 'field_integer_two'];
    foreach ($fnames as $fname) 
    {
        $field = field_info_field($fname);
        if ($field)
        {
            db_query("UPDATE field_config SET type = 'number_decimal' WHERE field_name = '".$fname."'");

            db_change_field('field_data_'.$fname, $fname.'_value', $fname.'_value', array(
                'type' => 'numeric',
                'precision' => 10,
                'scale' => 2,
                'not null' => FALSE,
            ));

            db_change_field('field_revision_'.$fname, $fname.'_value', $fname.'_value', array(
                'type' => 'numeric',
                'precision' => 10,
                'scale' => 2,
                'not null' => FALSE,
            ));
            
            $field['type'] = 'number_decimal';
            $field['settings'] = [
                'precision' => 10,
                'scale' => 2,
                'decimal_separator' => '.',
            ];
            $field['columns']['value'] = [
                'type' => 'numeric',
                'precision' => 10,
                'scale' => 2,
                'not null' => FALSE,
            ];

            $instance_info = field_info_instance('node', $fname, 'formacion');

            $instance_info['display']['default']['type'] = 'number_decimal';
            $instance_info['display']['full']['type'] = 'number_decimal';
            $instance_info['display']['settings'] = [
                'precision' => 10,
                'scale' => 2,
                'decimal_separator' => '.',
            ];
            $instance_info['display']['full']['settings'] = [
                'precision' => 10,
                'scale' => 2,
                'decimal_separator' => '.',
            ];
            field_update_instance($instance_info);
            $serilized_field = serialize($field);
            // this is the only way i found to update field_config table. field_update_field do not work properly
            db_query("update field_config set data= :data where field_name= :name", [':data' => $serilized_field, ':name' => $fname]);

        }
    }
}


 

También te puede interesar: