Add internal link

Le type d'entité InternalLink

Après avoir généré notre module pour la Création d'un module avec Drupal 8, puis notre nouveau type d'entité internal_link, nous allons pouvoir avancer.

Analyse

Penchons-nous sur le code du fichier src/Entity/InternalLink.php.

/**
 * @file
 * Contains \Drupal\internal_link\Entity\InternalLink.
 */
 
namespace Drupal\internal_link\Entity;
 
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\internal_link\InternalLinkInterface;
use Drupal\user\UserInterface;
 
/**
 * Defines the Internal link entity.
 *
 * @ingroup internal_link
 *
 * @ContentEntityType(
 *   id = "internal_link",
 *   label = @Translation("Internal link"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\internal_link\InternalLinkListBuilder",
 *     "views_data" = "Drupal\internal_link\Entity\InternalLinkViewsData",
 *
 *     "form" = {
 *       "default" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "add" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "edit" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "delete" = "Drupal\internal_link\Form\InternalLinkDeleteForm",
 *     },
 *     "access" = "Drupal\internal_link\InternalLinkAccessControlHandler",
 *     "route_provider" = {
 *       "html" = "Drupal\internal_link\InternalLinkHtmlRouteProvider",
 *     },
 *   },
 *   base_table = "internal_link",
 *   admin_permission = "administer internal link entities",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "user_id",
 *     "langcode" = "langcode",
 *     "status" = "status",
 *   },
 *   links = {
 *     "canonical" = "/admin/structure/internal_link/{internal_link}",
 *     "add-form" = "/admin/structure/internal_link/add",
 *     "edit-form" = "/admin/structure/internal_link/{internal_link}/edit",
 *     "delete-form" = "/admin/structure/internal_link/{internal_link}/delete",
 *     "collection" = "/admin/structure/internal_link",
 *   },
 *   field_ui_base_route = "internal_link.settings"
 * )
 */
class InternalLink extends ContentEntityBase implements InternalLinkInterface {
  use EntityChangedTrait;
  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
    parent::preCreate($storage_controller, $values);
    $values += array(
      'user_id' => \Drupal::currentUser()->id(),
    );
  }
 
  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->get('name')->value;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setName($name) {
    $this->set('name', $name);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('created')->value;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('created', $timestamp);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('user_id')->entity;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('user_id')->target_id;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('user_id', $uid);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('user_id', $account->id());
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function isPublished() {
    return (bool) $this->getEntityKey('status');
  }
 
  /**
   * {@inheritdoc}
   */
  public function setPublished($published) {
    $this->set('status', $published ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('The ID of the Internal link entity.'))
      ->setReadOnly(TRUE);
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('The UUID of the Internal link entity.'))
      ->setReadOnly(TRUE);
 
    $fields['user_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Authored by'))
      ->setDescription(t('The user ID of author of the Internal link entity.'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', array(
        'label' => 'hidden',
        'type' => 'author',
        'weight' => 0,
      ))
      ->setDisplayOptions('form', array(
        'type' => 'entity_reference_autocomplete',
        'weight' => 5,
        'settings' => array(
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ),
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
 
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Name'))
      ->setDescription(t('The name of the Internal link entity.'))
      ->setSettings(array(
        'max_length' => 50,
        'text_processing' => 0,
      ))
      ->setDefaultValue('')
      ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'string',
        'weight' => -4,
      ))
      ->setDisplayOptions('form', array(
        'type' => 'string_textfield',
        'weight' => -4,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
 
    $fields['status'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Publishing status'))
      ->setDescription(t('A boolean indicating whether the Internal link is published.'))
      ->setDefaultValue(TRUE);
 
    $fields['langcode'] = BaseFieldDefinition::create('language')
      ->setLabel(t('Language code'))
      ->setDescription(t('The language code for the Internal link entity.'))
      ->setDisplayOptions('form', array(
        'type' => 'language_select',
        'weight' => 10,
      ))
      ->setDisplayConfigurable('form', TRUE);
 
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));
 
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));
 
    return $fields;
  }
}

Nous y voyons les choses suivantes :

  • une annotation du type @ContentEntityType qui déclare une série de métadonnées;
  • une déclaration de classe contenant:
    • une implémentation basique de la fonction publique statique preCreate;
    • une série de "getter" et "setter";
    • et enfin une implémentation basique de la fonction publique statique baseFieldDefinitions.

Multilingue

Nous allons tout d'abord modifier l'annotation. A la base, notre type d'entité n'est pas traduisible mais notre volonté est justement de faire en sorte que les utilisateurs puissent traduire chaque entité de type internal_link. Pour ce faire, nous allons rajouter 2 métadonnées:

 *   base_table = "internal_link",
+ *   data_table = "internal_link_field_data",
+ *   translatable = TRUE,
 *   admin_permission = "administer internal link entities",
 *   entity_keys = {

Les 2 lignes ci-dessus précédées d'un + indiquent à Drupal que les données devront être stockées dans la table "internal_link_field_data" et que l'entité devra être traduisible.

Activation du module

Cet ajout étant fait, nous pouvons ENFIN activer notre module !! Activer le module avant ça aurait pu causer des problèmes car l'indication "data_table" indique à Drupal qu'il doit générer une table supplémentaire dans le schéma de base de données. Dans le terminal, nous entrons la ligne de commande suivante et validons:

MacPro:drupal8 titouille$ drupal module:install internal_link
 Installing module(s) internal_link
 
 [OK] The following module(s) were installed successfully: internal_link                                                
 
 // cache:rebuild
 
 Rebuilding cache(s), wait a moment please.
 
 [OK] Done clearing cache(s).                                                                                           
 
MacPro:drupal8 titouille$ 

Nous avons maintenant accès aux url's suivantes :

  • admin/config/search/internal_link/settings: la partie configuration pour notre type de contenu internal_link.
  • admin/structure/internal_link: la partie gestion de nos liens internes

Nous devons maintenant nous atteler à l'implémentation de notre type d'entité.

Champs à déclarer

La première chose à faire est de définir les champs qui vont être utiles. Dans le cadre de liens internes tels que nous voulons les proposer dans le système, les champs suivants devront être déclarés:

  • Language (langcode): la langue de l'entité, sous forme de liste déroulante;
  • Mode du lien (mode): une liste déroulante proposant les choix suivants: manuel, automatique, les deux;
  • Mot/phrase (name): un champ texte pour indiquer le mot ou la phrase qui sera remplacée par le lien;
  • Sensibilité à la casse (case sensitive): une case à cocher permettra de décider si le parser devra être sensible à la casse lorsqu'il chercher le mot/la phrase à remplacer;
  • Type d'url (url_type): nous allons proposer une liste déroulante avec deux types d'url possible. Soit une url standard type http://, soit une url dérivée d'un type d'entité;
  • Url standard (url): si l'utilisateur choisi le type d'url "standard", il indiquera dans un champ texte l'url à utiliser;
  • Type d'entité (url_entity_type): si l'utilisateur choisi le type d'url "entité", on lui demandera de choisir dans une liste déroulante quel sera le type d'entité vers lesquel il veut rediriger;
  • Entité (entity_id): si l'utilisateur choisi le type d'url "entité", on lui demandera d'indiquer quelle sera l'entité vers laquelle il voudra rediriger. Un champ autocomplete permettra d'afficher les entités du type choisi au préalable;
  • Titre (url_title): un champ texte pour indiquer la valeur de l'attribut "title" du lien qui viendra remplacer le mot/la phrase recherchée;
  • Poids (weight): une valeur numérique spécifiant le "poids" du mot à rechercher, pour donner plus ou moins de poids à des mots/des phrases précises;
  • Statut (status): une case à cocher indiquant si l'entité en question est active ou non;
  • Classe (class): un champ texte définissant une ou plusieurs classes à appliquer en tant qu'attribut "class" du lien;
  • Relation (rel): un champ texte définissant la valeur de l'attribut "rel" du lien;
  • Visibilité (visibility): deux boutons radios indiquant des valeurs du type: "appliquable partout sauf sur les chemins suivants" et "appliquable uniquement sur les chemins suivants";
  • Liste de chemins (except_list): une zone de texte multiligne pour spécifier une liste de chemins, qui seront soumis au choix de visibilité;

Définition de constantes

Concernant le "mode" du lien, nous allons d'abord définir 3 constantes dans le fichier .module, afin de pouvoir les réutiliser dans notre code:

const INTERNAL_LINK_MODE_ALL = 1;
const INTERNAL_LINK_MODE_MANUAL = 2;
const INTERNAL_LINK_MODE_AUTO = 4;

Ajout d'un compteur

Nous avons ensuite besoin d'un "compteur" dans notre entité, qui sera utilisé pour des besoins précis. Une variable $count ainsi que des getter/setter, et une méthode d'initialisation du compteur feront l'affaire.

Getter

Nous définissons également différents getter pour récupérer des données calculées, tels que :

public function isVisible() {...}
public function getFinalUrl() {...}
public function getUrlData($name = NULL, $list_builder = FALSE) {...}
public function getHighlightedData($name = NULL) {...}

Ces méthodes vont nous permettre d'implémenter des tests et de retourner des informations en interrogeant directement l'entité elle-même, en lieu et place d'avoir des méthodes à qui nous devrions passer les entités et accéder à leurs données pour effectuer les tests et retourner les informations résultantes.

La méthode baseFieldDefinitions

Nous implémentons la fonction baseFieldDefinitions afin de déclarer tous les champs qui devront être affichés dans le formulaire et en mode visualisation:

/**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('The ID of the Internal link entity.'))
      ->setReadOnly(TRUE);
 
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('The UUID of the Internal link entity.'))
      ->setReadOnly(TRUE);
 
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'))
      ->setDisplayOptions('form', [
        'type' => 'datetime_timestamp',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));
 
 
 
    $fields['langcode'] = BaseFieldDefinition::create('language')
      ->setLabel(t('Language code'))
      ->setDescription(t('The language code for the Internal link entity.'))
      ->setDisplayOptions('form', [
        'type' => 'language_select',
        'weight' => -10,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $modes = InternalLink::getModes();
 
    $fields['mode'] = BaseFieldDefinition::create('list_integer')
      ->setLabel(t('Mode'))
      ->setDescription(t('The mode of application.'))
      ->setSetting('unsigned', TRUE)
      ->setRequired(TRUE)
      ->setSetting('allowed_values', $modes)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 0,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 0,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Word/Phrase'))
      ->setDescription(t('The word or phrase you wish to convert to a link. This field is case sensitive.'))
      ->setSettings([
        'max_length' => 100,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 2,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 2,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['case_sensitive'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Case sensitive'))
      ->setDescription(t('By default Internal Link are case sensitive. Uncheck the checkbox if you want this particular Internal Link to be case insensitive.'))
      ->setDefaultValue(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4,
      ])
      ->setDisplayOptions('form', [
        'settings' => ['display_label' => TRUE],
        'weight' => 4,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url_type'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('URL type'))
      ->setDescription(t('The type of URL: standard link or entity link.'))
      ->setRequired(TRUE)
      ->setTranslatable(TRUE)
      ->setSetting('allowed_values', InternalLink::getUrlTypes())
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4.5,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 4.5,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url'] = BaseFieldDefinition::create('string')
      ->setLabel(t('URL'))
      ->setDescription(t('The URL of the page to link to. External links must start with %http or %https and will be open in new window.', ['%http' => 'http://', '%https' => 'https://']))
      ->setTranslatable(TRUE)
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4.8,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 4.8,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $entity_types = [];
    $etm = \Drupal::entityTypeManager();
    foreach($etm->getDefinitions() as $id => $definition) {
      if (is_a($definition , 'Drupal\Core\Entity\ContentEntityType')
          && $id != 'internal_link') {
        $entity_types[$id] = $definition->getLabel();
      }
    }
 
    $fields['url_entity_type'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Entity type'))
      ->setDescription(t('The entity type to find url. Select an entity before search the matching URL.'))
      ->setSetting('allowed_values', $entity_types)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 5,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'empty_value' => '_none',
        'empty_label' => t('None'),
        'weight' => 5,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['entity_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Entity'))
      ->setDescription(t('The entity to link to.'))
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 6,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 6,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url_title'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Url title'))
      ->setDescription(t('Title for the above URL. It will be embedded in the created link and appear as a tooltip when hovering the mouse over the link.'))
      ->setTranslatable(TRUE)
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 8,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 8,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['class'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Class'))
      ->setDescription(t('Use this to add a class for the link. Default value is "internal-link".'))
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('internal-link')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 10,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['rel'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Relationship'))
      ->setDescription(t('Use this to add a rel attribute to the link.'))
      ->setSettings([
        'max_length' => 50,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 12,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 12,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['visibility'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Visibility'))
      ->setDescription(t('Show links on specific pages'))
      ->setSetting('unsigned', TRUE)
      ->setRequired(TRUE)
      ->setDefaultValue(0)
      ->setSetting('on_label', t('Only the listed pages'))
      ->setSetting('off_label', t('All pages except those listed'))
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 14,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_buttons',
        'weight' => 14,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['except_list'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Except list'))
      ->setDescription(t('Specify pages by using their paths. Enter one path per line. E.g. node/1.'))
      ->setTranslatable(TRUE)
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 16,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textarea',
        'weight' => 16,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['weight'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Weight'))
      ->setDescription(t('Weight of the word. Lighter weights are higher up, heavier weights go down.'))
      ->setSetting('unsigned', FALSE)
      ->setRequired(TRUE)
      ->setDefaultValue(0)
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 18,
      ])
      ->setDisplayOptions('form', [
        'type' => 'number',
        'weight' => 18,
      ]);
 
 
 
 
    $fields['status'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Publishing status'))
      ->setDescription(t('A boolean indicating whether the Internal link is published.'))
      ->setDefaultValue(TRUE)
      ->setTranslatable(TRUE)
      ->setDisplayOptions('form', [
        'type' => 'checkbox',
        'weight' => 30,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Authored by'))
      ->setDescription(t('The user ID of author of the Internal link entity.'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'author',
        'weight' => 32,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 32,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
 
    return $fields;
  }

Dans cette méthode, nous trouvons les champs "par défaut" qui sont utiles à l'entité : id, uuid, name (qui se nommait à la base "label" mais que nous avons modifié), uid, langcode et status.

Nous retrouvons ces champs dans l'annotation d'entité, en tant que "entity_keys":

/**
 *   ...
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "uid",
 *     "langcode" = "langcode",
 *     "status" = "status",
 *   },
 *   ...
*/

Nous trouvons également deux champs "created" et "changed" qui vont permettre de stocker la date de création et de dernière modification.

Nous trouvons enfin tous les champs cités auparavant: modecase_sensitive, url_type, url, url_entity_type, entity_id, url_title, class, rel, visibility, except_list, weight.

La déclaration d'un champ se fait en utilisant la méthode BaseFieldDefinition::create([type de donnée]) puis en indiquant les propriétés et options via des méthodes telles que ->setLabel(), ->setTranslatable(), ->setSettings(). L'avantage de cette méthode de création est qu'elle est "chaînable", c'est à dire que chaque setter retourne l'objet créé, ce qui permet d'enchaîner les appels.

Nous noterons entre autre que

  • ->setSettings() permet de passer des options de configuration du type de données,
  • ->setDefaultValue() permet d'indiquer la ou les valeurs par défaut,
  • ->setDisplayOptions() prend en premier argument le mode d'affichage (view ou form) et en second paramètre les options du formatteur (mode view) ou du champ (mode form)
  • ->setDisplayConfigurable() prend en premier argument le mode d'affichage (view ou form) et en second argument le statut du champ concernant la possibilité de le configurer ou non.

Ce qui est intéressant, c'est que cette méthode baseFieldDefinitions nous permet de créer de A à Z les deux modes d'affichage de notre entité, et que ces informations seront automatiquement ajoutées à la base de données dans les tables adéquates.

Le champ name devra être de type autocomplete, et le champ entity_id également. Ce dernier possède un type de données "entity_reference" et un widget de type "entity_reference_autocomplete", ce qui nous simplifiera la vie par la suite (ou pas...)

Code complet

Au final, voilà à quoi ressemble le fichier InternalLink.php:

/**
 * @file
 * Contains \Drupal\internal_link\Entity\InternalLink.
 */
 
namespace Drupal\internal_link\Entity;
 
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\internal_link\InternalLinkInterface;
use Drupal\user\UserInterface;
 
/**
 * Defines the Internal link entity.
 *
 * @ingroup internal_link
 *
 * @ContentEntityType(
 *   id = "internal_link",
 *   label = @Translation("Internal link"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\internal_link\InternalLinkListBuilder",
 *     "views_data" = "Drupal\internal_link\Entity\InternalLinkViewsData",
 *
 *     "form" = {
 *       "default" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "add" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "edit" = "Drupal\internal_link\Form\InternalLinkForm",
 *       "delete" = "Drupal\internal_link\Form\InternalLinkDeleteForm",
 *     },
 *     "access" = "Drupal\internal_link\InternalLinkAccessControlHandler",
 *     "route_provider" = {
 *       "html" = "Drupal\internal_link\InternalLinkHtmlRouteProvider",
 *     },
 *   },
 *   base_table = "internal_link",
 *   data_table = "internal_link_field_data",
 *   translatable = TRUE,
 *   admin_permission = "administer internal link entities",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "uid",
 *     "langcode" = "langcode",
 *     "status" = "status",
 *   },
 *   links = {
 *     "canonical" = "/admin/structure/internal_link/{internal_link}",
 *     "add-form" = "/admin/structure/internal_link/add",
 *     "edit-form" = "/admin/structure/internal_link/{internal_link}/edit",
 *     "delete-form" = "/admin/structure/internal_link/{internal_link}/delete",
 *     "collection" = "/admin/structure/internal_link",
 *   },
 *   field_ui_base_route = "internal_link.settings"
 * )
 */
class InternalLink extends ContentEntityBase implements InternalLinkInterface {
  use EntityChangedTrait;
 
  /**
   * Number of time we found this link to replace in text.
   * 
   * @var integer
   */
  private $count = 0;
  /**
   * Get counter.
   * 
   * @return integer
   *   The current counter value.
   */
  public function getCount() {
    return $this->count;
  }
  /**
   * Set counter.
   * 
   * @param integer $increment
   *   The counter increment.
   */
  public function setCount($increment) {
    $this->count += $increment;
  }
  /**
   * Initialize counter.
   */
  public function initializeCount() {
    $this->count = 0;
  }
 
  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
 
    parent::preCreate($storage_controller, $values);
    $values += [
      'uid' => \Drupal::currentUser()->id(),
    ];
  }
 
 
  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->get('name')->value;
  }
 
  /**
   * Get the name as lowercase.
   * 
   * @return string
   *   Lower cased name.
   */
  public function getLowerName() {
    return strtolower($this->getName());
  }
 
  /**
   * {@inheritdoc}
   */
  public function setName($name) {
    $this->set('name', $name);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('created')->value;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('created', $timestamp);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('uid')->entity;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('uid')->target_id;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('uid', $uid);
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('uid', $account->id());
    return $this;
  }
 
  /**
   * {@inheritdoc}
   */
  public function isPublished() {
    return (bool) $this->getEntityKey('status');
  }
 
  /**
   * {@inheritdoc}
   */
  public function setPublished($published) {
    $this->set('status', $published ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
    return $this;
  }
 
 
  /**
   * Get all possible modes.
   * 
   * @return array
   *   An associative array with key/label
   */
  public static function getModes() {
    return [
      INTERNAL_LINK_MODE_ALL => t('All modes'),
      INTERNAL_LINK_MODE_MANUAL => t('Manual'),
      INTERNAL_LINK_MODE_AUTO => t('Automatic'),
    ];
  }
 
  /**
   * Retrieve mode.
   * 
   * @return integer
   *   The internal link mode to use.
   */
  public function getMode() {
    return $this->get('mode')->value;
  }
 
  /**
   * Get mode label switch mode data.
   * 
   * @param integer $mode
   *   The mode as integer.
   * 
   * @return string
   *   The mode label
   * 
   * @throws \RuntimeException
   * @see InternalLink::getModes().
   */
  public function getModeLabel($mode) {
    $modes = InternalLink::getModes();
    if (isset($modes[$mode])) {
      return $modes[$mode];
    }
    throw new \RuntimeException(t('%mode is not a valid internal-link mode.', ['%mode' => $mode]));
  }
 
  /**
   * Retrieve case sensitive state.
   * 
   * @return boolean
   *   Case sensitive state.
   */
  public function getCaseSensitive() {
    return $this->get('case_sensitive')->value;
  }
 
  /**
   * Get all possible url types.
   * 
   * @return array
   *   An associative array with key/label
   */
  public static function getUrlTypes() {
    return [
      'standard' => t('Standard'),
      'entity' => t('Entity'),
    ];
  }
 
  /**
   * Get type of url.
   * 
   * @return string
   *   The type of URL.
   * 
   * @see InternalLink::getUrlTypes().
   */
  public function getUrlType() {
    return $this->get('url_type')->value;
  }
 
  /**
   * Get URL type label switch url type data.
   * 
   * @param string $url_type
   *   The type of URL.
   * @return string
   *   The URL type label.
   * @throws \RuntimeException
   */
  public function getUrlTypeLabel($url_type) {
    $url_types = $this->getUrlTypes();
    if (isset($url_types[$url_type])) {
      return $url_types[$url_type];
    }
    throw new \RuntimeException(t('%url_type is not a valid internal-link url type.', ['%url_type' => $url_type]));
  }
 
  /**
   * Get URL value.
   * 
   * @return string
   *   The URL value.
   */
  public function getUrl() {
    return $this->get('url')->value;
  }
 
  /**
   * Get the URL entity type.
   * 
   * @return string
   *   The type o entity type used to get URL.
   */
  public function getUrlEntityType() {
    return $this->get('url_entity_type')->value;
  }
 
  /**
   * Get the selected entity ID.
   * 
   * @return integer
   *   The selected entity ID.
   */
  public function getEntityId() {
    return $this->get('entity_id')->target_id;
  }
 
  /**
   * Get the selected entity.
   * 
   * @return EntityInterface
   *   The selected entity if url-type is "entity", else NULL.
   */
  public function getEntity() {
    $entity = NULL;
    if ($this->getUrlType() == 'entity') {
      $entity = $this->entityTypeManager()->getStorage($this->getUrlEntityType())->load($this->getEntityId());
    }
    return $entity;
  }
 
  /**
   * Get title to apply to link.
   * 
   * @return string
   *   The title of the link.
   */
  public function getUrlTitle() {
    return $this->get('url_title')->value;
  }
  /**
   * Get class(es) to apply to link.
   * 
   * @return string
   *   The class(es) of the link.
   */
  public function getClass() {
    return $this->get('class')->value;
  }
  /**
   * Get relationship to apply to link (rel attribute).
   * 
   * @return string
   *   The relationship of the link.
   */
  public function getRel() {
    return $this->get('rel')->value;
  }
 
  /**
   * Get data to display if "Highlight words" setting
   * is requested instead of display complete link.
   * 
   * @param string $name
   *   The string to highlight.
   * 
   * @return string
   *   The final highlighted link.
   */
  public function getHighlightedData($name = NULL) {
    return '';
  }
 
  /**
   * Get URL data to display.
   * 
   * @param string $name
   *   The word to use in the link.
   * @param boolean $list_builder
   *   To know if we must embed all informations
   *   in tooltip text or only the title. Useful
   *   when displaying links in administration lists.
   * 
   * @return string
   *   The final builded URL.
   */
  public function getUrlData($name = NULL, $list_builder = FALSE) {
    return '';
  }
 
  /**
   * Get visibility state.
   * 
   * @return boolean
   *   Indicate the visibility state: 
   *   - 0 = visible on all pages except listed pages;
   *   - 1 = only visible on listed pages;
   */
  public function getVisibility() {
    return $this->get('visibility')->value;
  }
 
  /**
   * Get exceptions pages list.
   * 
   * @return string
   *   List of paths used by visibility settings.
   */
  public function getExceptList() {
    return $this->get('except_list')->value;
  }
 
  /**
   * Get final URL value switch URL data.
   * 
   * @return string
   *   The final URL value.
   */
  public function getFinalUrl() {
    return '';
  }
 
  /**
   * Test visibility of entity.
   * We check if the current entity must
   * be applied on the current page.
   * 
   * @return boolean
   *   TRUE if the current entity must be added to parsing, else FALSE.
   */
  public function isVisible() {
    return TRUE;
  }
 
  /**
   * Get weight.
   *
   * @return integer
   *   weight of entity.
   */
  public function getWeight() {
    return $this->get('weight')->value;
  }
 
  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('The ID of the Internal link entity.'))
      ->setReadOnly(TRUE);
 
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('The UUID of the Internal link entity.'))
      ->setReadOnly(TRUE);
 
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'))
      ->setDisplayOptions('form', [
        'type' => 'datetime_timestamp',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));
 
 
 
    $fields['langcode'] = BaseFieldDefinition::create('language')
      ->setLabel(t('Language code'))
      ->setDescription(t('The language code for the Internal link entity.'))
      ->setDisplayOptions('form', [
        'type' => 'language_select',
        'weight' => -10,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $modes = InternalLink::getModes();
 
    $fields['mode'] = BaseFieldDefinition::create('list_integer')
      ->setLabel(t('Mode'))
      ->setDescription(t('The mode of application.'))
      ->setSetting('unsigned', TRUE)
      ->setRequired(TRUE)
      ->setSetting('allowed_values', $modes)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 0,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 0,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Word/Phrase'))
      ->setDescription(t('The word or phrase you wish to convert to a link. This field is case sensitive.'))
      ->setSettings([
        'max_length' => 100,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 2,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 2,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['case_sensitive'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Case sensitive'))
      ->setDescription(t('By default Internal Link are case sensitive. Uncheck the checkbox if you want this particular Internal Link to be case insensitive.'))
      ->setDefaultValue(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4,
      ])
      ->setDisplayOptions('form', [
        'settings' => ['display_label' => TRUE],
        'weight' => 4,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url_type'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('URL type'))
      ->setDescription(t('The type of URL: standard link or entity link.'))
      ->setRequired(TRUE)
      ->setTranslatable(TRUE)
      ->setSetting('allowed_values', InternalLink::getUrlTypes())
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4.5,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 4.5,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url'] = BaseFieldDefinition::create('string')
      ->setLabel(t('URL'))
      ->setDescription(t('The URL of the page to link to. External links must start with %http or %https and will be open in new window.', ['%http' => 'http://', '%https' => 'https://']))
      ->setTranslatable(TRUE)
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 4.8,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 4.8,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $entity_types = [];
    $etm = \Drupal::entityTypeManager();
    foreach($etm->getDefinitions() as $id => $definition) {
      if (is_a($definition , 'Drupal\Core\Entity\ContentEntityType')) {
        $entity_types[$id] = $definition->getLabel();
      }
    }
 
    $fields['url_entity_type'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Entity type'))
      ->setDescription(t('The entity type to find url. Select an entity before search the matching URL.'))
      ->setSetting('allowed_values', $entity_types)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 5,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'empty_value' => '_none',
        'empty_label' => t('None'),
        'weight' => 5,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['entity_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Entity'))
      ->setDescription(t('The entity to link to.'))
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 6,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 6,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['url_title'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Url title'))
      ->setDescription(t('Title for the above URL. It will be embedded in the created link and appear as a tooltip when hovering the mouse over the link.'))
      ->setTranslatable(TRUE)
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 8,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 8,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['class'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Class'))
      ->setDescription(t('Use this to add a class for the link. Default value is "internal-link".'))
      ->setSettings([
        'max_length' => 255,
        'text_processing' => 0,
      ])
      ->setDefaultValue('internal-link')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 10,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['rel'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Relationship'))
      ->setDescription(t('Use this to add a rel attribute to the link.'))
      ->setSettings([
        'max_length' => 50,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 12,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textfield',
        'weight' => 12,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['visibility'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Visibility'))
      ->setDescription(t('Show links on specific pages'))
      ->setSetting('unsigned', TRUE)
      ->setRequired(TRUE)
      ->setDefaultValue(0)
      ->setSetting('on_label', t('Only the listed pages'))
      ->setSetting('off_label', t('All pages except those listed'))
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 14,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_buttons',
        'weight' => 14,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['except_list'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Except list'))
      ->setDescription(t('Specify pages by using their paths. Enter one path per line. E.g. node/1.'))
      ->setTranslatable(TRUE)
      ->setDefaultValue('')
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 16,
      ])
      ->setDisplayOptions('form', [
        'type' => 'textarea',
        'weight' => 16,
      ])
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
 
 
    $fields['weight'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Weight'))
      ->setDescription(t('Weight of the word. Lighter weights are higher up, heavier weights go down.'))
      ->setSetting('unsigned', FALSE)
      ->setRequired(TRUE)
      ->setDefaultValue(0)
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 18,
      ])
      ->setDisplayOptions('form', [
        'type' => 'number',
        'weight' => 18,
      ]);
 
 
 
 
    $fields['status'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Publishing status'))
      ->setDescription(t('A boolean indicating whether the Internal link is published.'))
      ->setDefaultValue(TRUE)
      ->setTranslatable(TRUE)
      ->setDisplayOptions('form', [
        'type' => 'checkbox',
        'weight' => 30,
      ])
      ->setDisplayConfigurable('form', TRUE);
 
    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Authored by'))
      ->setDescription(t('The user ID of author of the Internal link entity.'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
      ->setTranslatable(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'author',
        'weight' => 32,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 32,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
 
    return $fields;
  }
}

Nous verrons l'implémentation de certaines méthodes utiles par la suite, lorsque nous en aurons réellement besoin.

Formulaire d'édition

En naviguant sur l'url /admin/structure/internal_link/add, nous voyons maintenant le formulaire d'ajout, qui devrait ressembler à ça:

Formulaire d'édition

Multilingue, encore

Il nous manque encore un dernier point: les traductions de notre type d'entité. Pour activer les traductions, il est d'abord nécessaire d'activer les modules nécessaires et d'ajouter une langue à notre site. Les modules à activer sont "Configuration translation" et "Content translation", afin d'avoir un site multilingue. J'ai ensuite ajouté l'anglais en passant par l'url /admin/config/regional/language. Il suffit de cliquer sur le bouton "+ Ajouter une langue" et sélectionner l'anglais. Cette action va lancer la procédure d'activation de la nouvelle langue.

Une fois cet ajout effectué, nous pouvons vider les caches avec Drupal Console via la commande

drupal cr all

Puis naviguer vers /admin/config/regional/content-language pour activer les traductions au niveau des types d'entité "Contenu" et "Internal link". Nous sélectionnons ces 2 types d'entité, nous cochons "Traduisible" à gauche de chaque bundle, ce qui nous affiche les champs qui doivent être traduits. J'ai rendu traduisible le bundle "Article" du type de contenu node, avec les champs Title, Alias d'URL, Corps, Image, Alt, Title et Etiquettes. Pour le type Internal link, j'ai coché les champs Word/Phrase, URL et Url title afin de rendre traduisible le mot à rechercher, l'url et le titre de l'url.

Si nous revenons maintenant sur l'url /admin/structure/internal_link/add, nous voyons qu'une liste déroulante proposant les langages disponibles est maintenant affichée en haut du formulaire.

Soyons sûr que tout est ok

Attention. Il est possible que l'ajout des champs de notre nouveau type d'entité n'ait pas été pris en compte correctement. Le formulaire s'affiche proprement, par contre, la table "internal_link_field_data" peut ne pas contenir la déclaration de tous les champs. Pour nous assurer que tout est bien conforme à nos besoins, nous allons désinstaller et réinstaller le module à l'aide de la commande suivante dans le terminal:

drupal module:uninstall internal_link && drupal module:install internal_link

Ainsi, nous avons la certitude que tout est installé correctement avec les bonnes colonnes dans la base de données.

Conclusion

Nous avons maintenant la base de notre type d'entité avec les champs nécessaires. Il va falloir personnaliser le formulaire d'édition, mais avant ça, nous allons nous pencher sur la notion de services et créer un service orienté données pour récupérer les entités dans la base de données.

Commentaires

Salut et merçi pour ce

Salut et merçi pour ce travail magnifique qui m'aide à me lancer dans le developpement sous drupal 8. Je suis cette démarche depuis le début mais j'ai une erreur lorsque je désinstalle et réinstalle le module Base table or view not found table internal_link doesn't exist

Salut Alain,

Salut Alain, Il est possible que le problème se situe au niveau de la modification entre le premier code source et le second. En fait, dans le premier, on ne trouve pas le "translatable" dans les métadonnées. Tu as peut-être activé le module avant de rajouter la partie "translatable", ce qui fait que si tu as modifié le module une fois installé, il ne trouve pas la table "data" correspondante (internal_link_field_data). J'étais tombé sur ce type de problème plusieurs fois... Le plus simple est de créer la table manuellement en y ajoutant le champ "id" uniquement, puis de retenter la désinstallation via drupal console. Ainsi il pourra désinstaller le module proprement en supprimant la table recherchée, puis il réinstallera le module en créant les tables nécessaires. Bon courage ;-)

Ajouter un commentaire

CAPTCHA
Cette question permet de savoir si vous êtes un visiteur ou un robot de soumission de spams