Découvrez comment changer le type d'un champ existant sans le supprimer.
Je vous partage aujourd’hui une astuce qui peut vous épargner de perdre quelques cheveux. Il s’agit de l’erreur suivante :
The SQL storage cannot change the schema for an existing field (field_meta_keywords in taxonomy_term entity) with data.
Cette erreur survient lorsque vous avez créé un champ et que vous avez configuré ses settings, que cette configuration est passée en production et que vos utilisateurs ont créé du contenu qui remplit les données de ce champ.
Il arrive que vous ayez besoin de modifier la configuration de ce champ. Simple me direz-vous ? Et bien non, pas toujours car Drupal ne va pas vous laisser faire (à raison).
Selon le type de champ que vous modifiez, les settings du champ peuvent être utilisés pour créer les colonnes de la table. Avec du contenu dans la table, hors de question de vous laisser faire n’importe quoi au risque de perdre des données. Drupal va donc lever une exception (FieldStorageDefinitionUpdateForbiddenException
) qui est assez explicite, pas de mise à jour du stockage du champ lorsque des données sont présentes.
Comment contourner cela ? #
Vous pourriez tenter de modifier directement le fichier .yml de configuration de l’instance du champ à la main mais cela ne contournerait pas votre problème, Drupal comprendrait toujours que vous voulez modifier la configuration et vous protégerait contre la perte de vos données.
La seule solution pour cela est de travailler à un plus bas niveau via un hook_update_N()
pour faire le changement de configuration. Il faudra travailler directement sur les fonctions de base de données pour manipuler les colonnes.
Voici donc le code qui permet de faire cela :
/**
* Changement du schéma de field_meta_keywords stocké dans la configuration.
*/
function project_social_update_8006() {
// Augmentation de la longueur max du champ de 255 à 500 caractères.
try {
// 1. On vide le cache de la définition des champs pour travailler les mains libres.
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
// 2. On récupère la définition de notre instance de champ depuis les fichiers de config et on la sauve.
$storage_definition = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('taxonomy_term')['field_meta_keywords'];
\Drupal::service('entity.last_installed_schema.repository')->setLastInstalledFieldStorageDefinition($storage_definition);
// 3. On récupère la définition du champ.
$field_schema = \Drupal::keyValue('entity.storage_schema.sql')->get('taxonomy_term.field_schema_data.field_meta_keywords');
// 4. On définit la nouvelle valeur de notre configuration.
$field_schema['taxonomy_term__field_meta_keywords']['fields']['field_meta_keywords_value']['length'] = 500;
// 6. On applique notre nouvelle configuration dans le stockage du champ.
\Drupal::keyValue('entity.storage_schema.sql')->set('taxonomy_term.field_schema_data.field_meta_keywords', $field_schema);
}
catch (SchemaException $e) {
\Drupal::logger('project_social')->error($e->getMessage());
}
}
La solution a été réalisée suite à ce patch très inspiré de Berdir qui montre la voie de l'implémentation : https://www.drupal.org/node/2641828#comment-10711058
Bien que l'on ait utilisé les fonctions de bas niveau pour faire les modifications, on aura tout de même exporté la configuration dans son état final pour s'assurer que le la longueur du champ de l'exemple est bien définie à 500 caractères. Cela permet de ne pas détecter de diff une fois notre correctif effectué. (L'export YML et la définition en base doivent être raccords).
Il est à noter évidemment que si vous aviez dû faire des modifications plus compliquée du type supprimer une colonne il aurait fallu des étapes supplémentaires pour ne pas perdre des données. Vous saurez maintenant comment vous y prendre.