Contourner l'erreur : "The SQL storage cannot change the schema for an existing field"

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.

 

 

Image de couverture par Florent Darraultvia Wikimedia Commons.

Commentaires

Kakoum Mardi 18 décembre 2018 - 17:22
Yop !   Merci c'est super :) J'ai tout de même un petit soucis, car j'essaie mais concretement il ne se passe rien. Dans la BDD mon varchar est toujours limité à un petit nombre de caractère. J'ai fais cela pour modifier le champ d'une entity custom. Pourtant j'arrive à bien récupérer mon field_schema, je change la valeur lenght, mais il ne se passe rien :'(

Votre commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Votre adresse servira à afficher un Gravatar et à vous notifier des réponses. Votre commentaire sera anonymisé si ce billet est dépublié pendant plus de 3 mois.
Pour lutter contre le spam notre système enregistre votre adresse IP et votre adresse e-mail si vous la partagez.
Nous vous invitons à consulter notre politique de confidentialité pour comprendre les traitements faits de ces données et comment les rectifier.

À propos de Julien

Co-fondateur - Scrum master & Expert technique

Utilisateur de Drupal depuis 2008, j’ai fait mes armes comme développeur chez Commerce Guys puis me suis mis à encadrer les nouveaux arrivants avant de donner des formations, participer aux avant ventes et accompagner les équipes au passage à Scrum.

Je suis impliqué dans la communauté française de Drupal depuis 2009, j’ai été tour à tour président puis vice-président de l’association Drupal France et francophonie entre 2011 et 2013.