Drupal 8 & Drupal 9 - ajouter une colonne à un champ personnalisé

Si vous avez créé un champ personnalisé dans drupal, associé en tant que champs, ou basefield à un type d'entité et que vous souhaitez le changer (ajouter un attribut par exemple cela peut s'avérer assez compliqué, surtout si votre champs à plusieurs instances dans différents types d'entités.

Je suis tombé sur un helper permettant de finalement réaliser ça simplement : https://gist.github.com/JPustkuchen/ce53d40303a51ca5f17ce7f48c363b9b#fi…

Comment s'y prendre ?

Commencez par modifier votre type de champ et principalement la méthode schema pour ajouter votre attribut

(dans mon cas le fichier est : web/modules/custom/mon_module/src/Plugin/Field/FieldType/ListedItem.php, j'ai ajouté la colonne shortcode.

<?php

namespace Drupal\mon_module\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;

/**
 * Defines the 'listed' field type.
 *
 * @FieldType(
 *   id = "listed",
 *   label = @Translation("Listed"),
 *   category = @Translation("General"),
 *   default_widget = "listed",
 * )
 */
class ListedItem extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {

    $properties['menu'] = DataDefinition::create('boolean')
      ->setLabel(t('Menu'));
    $properties['pushs'] = DataDefinition::create('boolean')
      ->setLabel(t('Pushs'));
    $properties['listing'] = DataDefinition::create('boolean')
      ->setLabel(t('Listing'));
    $properties['shortcode'] = DataDefinition::create('boolean')
      ->setLabel(t('Shortcode'));

    return $properties;
  }


  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {

    $columns = [
      'menu' => [
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
      ],
      'pushs' => [
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
      ],
      'listing' => [
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
      ],
      'shortcode' => [
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
      ],
    ];

    $schema = [
      'columns' => $columns,
    ];

    return $schema;
  }

  /**
   * {@inheritdoc}
   */
  public static function generateSampleValue(FieldDefinitionInterface $field_definition) {

    $values['menu'] = (bool) mt_rand(0, 1);

    $values['pushs'] = (bool) mt_rand(0, 1);

    $values['listing'] = (bool) mt_rand(0, 1);

    $values['shortcode'] = (bool) mt_rand(0, 1);

    return $values;
  }

}

 

Puis copiez l'intégralité du helper dans votre fichier mon_module.install

function _field_type_schema_column_add_helper($field_type, array $columns_to_add = array()) {
  $processed_fields = [];
  $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  $field_definition = $field_type_manager->getDefinition($field_type);
  $field_item_class = $field_definition['class'];

  $schema = \Drupal::database()->schema();
  $entity_type_manager = \Drupal::entityTypeManager();
  $entity_field_manager = \Drupal::service('entity_field.manager');
  $entity_field_map = $entity_field_manager->getFieldMapByFieldType($field_type);
  // The key-value collection for tracking installed storage schema.
  $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql');
  $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed');

  foreach ($entity_field_map as $entity_type_id => $field_map) {
    $entity_storage = $entity_type_manager->getStorage($entity_type_id);

    // Only SQL storage based entities are supported / throw known exception.
    //    if (!($entity_storage instanceof SqlContentEntityStorage)) {
    //      continue;
    //    }

    $entity_type = $entity_type_manager->getDefinition($entity_type_id);
    $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id);
    /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
    $table_mapping = $entity_storage->getTableMapping($field_storage_definitions);
    // Only need field storage definitions of address fields.
    /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */
    foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) {
      $field_name = $field_storage_definition->getName();
      try {
        $table = $table_mapping->getFieldTableName($field_name);
      } catch (SqlContentEntityStorageException $e) {
        // Custom storage? Broken site? No matter what, if there is no table
        // or column, there's little we can do.
        continue;
      }
      // See if the field has a revision table.
      $revision_table = NULL;
      if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) {
        if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
          $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
        }
        elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
          $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable();
        }
      }
      // Load the installed field schema so that it can be updated.
      $schema_key = "$entity_type_id.field_schema_data.$field_name";
      $field_schema_data = $entity_storage_schema_sql->get($schema_key);

      $processed_fields[] = [$entity_type_id, $field_name];
      // Loop over each new column and add it as a schema column change.
      foreach ($columns_to_add as $column_id) {
        $column = $table_mapping->getFieldColumnName($field_storage_definition, $column_id);
        // Add `initial_from_field` to the new spec, as this will copy over
        // the entire data.
        $field_schema = $field_item_class::schema($field_storage_definition);
        $spec = $field_schema['columns'][$column_id];

        // Add the new column.
        $schema->addField($table, $column, $spec);
        if ($revision_table) {
          $schema->addField($revision_table, $column, $spec);
        }

        // Add the new column to the installed field schema.
        if ($field_schema_data) {
          $field_schema_data[$table]['fields'][$column] = $field_schema['columns'][$column_id];
          $field_schema_data[$table]['fields'][$column]['not null'] = FALSE;
          if ($revision_table) {
            $field_schema_data[$revision_table]['fields'][$column] = $field_schema['columns'][$column_id];
            $field_schema_data[$revision_table]['fields'][$column]['not null'] = FALSE;
          }
        }
      }

      // Save changes to the installed field schema.
      if ($field_schema_data) {
        $recipient_column = $table_mapping->getFieldColumnName($field_storage_definition, 'recipient');
        unset($field_schema_data[$table]['fields'][$recipient_column]);
        if ($revision_table) {
          unset($field_schema_data[$revision_table]['fields'][$recipient_column]);
        }
        $entity_storage_schema_sql->set($schema_key, $field_schema_data);
      }
      if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
        $key = "$entity_type_id.field_storage_definitions";
        if ($definitions = $entity_definitions_installed->get($key)) {
          $definitions[$field_name] = $field_storage_definition;
          $entity_definitions_installed->set($key, $definitions);
        }
      }
    }
  }
}

Ensuite créez une fonction d'update qui va modifier les schémas de l'ensemble des instance de votre champs en appelant le helper.

Dans mon cas, mon id de champs est listed et je souhaite ajouter la colonne shortcode :

/**
 * node.listed add column shortcode
 */
function mon_module_update_8039() {
  _field_type_schema_column_add_helper('listed', ['shortcode']);
}

Et voila !

Sur le lien mentionné il y a d'autres exemples pour d'autres usages, que je n'ai pas tester.

Contenus en rapport

Ajouter un commentaire

Ne sera pas publié
CAPTCHA
Désolé, pour ça, mais c'est le seul moyen pour éviter le spam...