Améliorer son référencement pour prendre en charge les types d'entités personnalisés dans Pathauto

Lorsque vous créez un type d'entité personnalisé, vous voulez bénéficier des fonctionnalités tierces liées aux types de contenu (vues, révisions, traduction...). La génération des alias pour le référencement avec Pathauto est l'un des points incontournables. Voyons comment connecter tout cela.

Vous venez de créer un nouveau type d'entité. Vous contemplez votre œuvre, réfléchissant à la prochaine touche à ajouter pour la parfaire. Tout à coup, un éclair vous frappe : "Il nous faut prendre en charge les alias Pathauto". Évidemment ! Pourquoi se priver de la puissance de cet outil ? C'est l'un des modules indispensables de Drupal 9 qui vous permet d'améliorer la qualité de votre référencement (du SEO éthique et responsable bien sûr !). Comme il est 16h et que la fin de journée approche, vous espérez pouvoir boucler ça rapidement pour reprendre votre marathon visionnage de l'intégrale de Columbo au plus vite.

Scénario 1 : Je suis une personne chanceuse

L'accident heureux consiste à vous rendre dans l'interface de Pathauto et de voir si votre type d'entité n'est pas déjà visible. Si tel est le cas, vous aurez le temps de vous faire un épisode de plus car la journée est terminée, cela fonctionne. Pour les gens moins chanceux, voici ce qu'il faut faire pour que cela fonctionne.

Scénario 2 : J'ai besoin de débrider le chemin de chaque entité

L'intérêt d'un système de gestion de contenu est de maîtriser les URLs de ses contenus. Vous pouvez opter pour de la génération automatique mais vouloir débrider cette génération entité par entité (comme pour les nœuds).

Pathauto est pré-configuré pour prendre en charge n'importe quel type d'entité. La magie se passe dans \Drupal\pathauto\Plugin\Deriver\EntityAliasTypeDeriver::getDerivativeDefinitions(). Trois critères sont à respecter pour que cela fonctionne clé en main.

1. Votre type d'entité doit déclarer un template de lien "canonical".

# inspectors/src/Entity/Inspector.php

/**
 * Defines the Inspector entity class.
 *
 * @ContentEntityType(
 *   ...
 *   links = {
 *     "add-form" = "/admin/content/inspector/add/{inspector}",
 *     "add-page" = "/admin/content/inspector/add",
 *     "canonical" = "/inspector/{inspector}",
 *     "edit-form" = "/admin/content/inspector/{inspector}/edit",
 *     "delete-form" = "/admin/content/inspector/{inspector}/delete",
 *     "collection" = "/admin/content/inspector"
 *   },
 *   ...
 * )
 */
class Inspector extends RevisionableContentEntityBase {}

2. Votre type d'entité doit être fieldable.

3. Votre bundle doit avoir un champ "path" qui stockera le chemin vers l'entité pour les cas où vous voulez débrider.

Si vous remplissez ces trois critères, votre type d'entité est maintenant pleinement utilisable dans Pathauto. Vous pouvez fermer votre CMS préféré et retourner à votre visionnage, vous n'avez raté que la première moitié de l'épisode.

Scénario 3 : Je n'ai pas de champ "path" ou n'en ai pas besoin

Si le champ "path" peut être intéressant dans certains cas, il arrivera que vos types d'entités soient plus basiques. Dans ce cas, comment l'exposer à Pathauto ? Il y a un petit peu plus de travail mais rien d'insurmontable. La solution consiste à implémenter un plugin AliasType. (Si vous ne savez pas comment fonctionnent les plugins, je vous invite à relire notre article). N'oubliez pas de déclarer son annotation @AliasType.

# inspectors/src/Plugin/pathauto/AliasType/InspectorsAliasType.php

/**
 * Defines the Inspector entity class.
 *
 * @AliasType(
*   id = "inspector_aliases",
*   label = @Translation("Inspectors"),
*   types = {"inspector"},
*   context_definitions = {
*     "inspector" = @ContextDefinition("entity:inspector")
*   }
 * )
 */
class InspectorsAliasType extends EntityAliasTypeBase {}

Les deux attributs de l'annotation à regarder de près sont types qui liste des types d'entités pour lesquels votre plugin va s'appliquer et context_definitions qui permet d'accéder à la liste des tokens de votre type d'entité (et donc ses champs, ses métadonnées et toutes les fonctionnalités avancées de Pathauto).

Ne vous précipitez pas tout de suite sur la génération de vos alias car vous risquez d'être déçu(e). La configuration est possible mais il reste une étape importante pour pouvoir profiter de tout cela, déclencher la génération. Si l'on ne donne pas un coup de pouce au système, la génération de vos alias ne se fera jamais. Pathauto implémente les hook_entity_OP() pour déclencher la génération mais si vous n'avez pas de canonical ou de champ path comme évoqué précédemment, vous devrez déclencher vous même le générateur.

Les alias doivent se créer, se mettre à jour et se supprimer en fonction de l'opération effectuée sur votre entité, vous allez donc devoir implémenter vous aussi les hook_entity_OP(). Comme on cible un type d'entité précis, les hooks à implémenter sont hook_ENTITY_TYPE_OP(). Cela donne ce code :

# inspectors.module

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function inspectors_inspector_update(Drupal\Core\Entity\EntityInterface $entity) {
  $generator = \Drupal::service('pathauto.generator');
  try {
    $generator->createEntityAlias($entity, 'update');
  }
  catch (\InvalidArgumentException $e) {
    $generator->messenger()->addError($e->getMessage());
  }
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function inspectors_inspector_insert(Drupal\Core\Entity\EntityInterface $entity) {
  $generator = \Drupal::service('pathauto.generator');
  try {
    $generator->createEntityAlias($entity, 'insert');
  }
  catch (\InvalidArgumentException $e) {
    $generator->messenger()->addError($e->getMessage());
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function inspectors_inspector_delete(EntityInterface $entity) {
  \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity);
}

Une fois vos hooks implémentés, vous devriez pouvoir tester la génération des alias et si tout s'est bien passé, cela devrait fonctionner.

Bonus : générer en masse les alias

Il vous reste une dernière action à faire si voulez bénéficier de la génération en masse des alias. Il faut implémenter la méthode \Drupal\mobility_plans\Plugin\pathauto\AliasType\MobilityPlanAliasType::bulkUpdate() dans votre plugin d'alias.

# inspectors/src/Plugin/pathauto/AliasType/InspectorsAliasType.php

class InspectorsAliasType extends EntityAliasTypeBase {

  /**
   * Update the URL aliases for multiple entities.
   *
   * @param array $ids
   *   An array of entity IDs.
   * @param array $options
   *   An optional array of additional options.
   *
   * @return int
   *   The number of updated URL aliases.
   */
  protected function bulkUpdate(array $ids, array $options = []) {
    $options += ['message' => FALSE];
    $updates = 0;
    $generator = \Drupal::service('pathauto.generator');

    $entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids);
    foreach ($entities as $entity) {
      // Update aliases for the entity's default language and its translations.
      foreach ($entity->getTranslationLanguages() as $langcode => $language) {
        $translated_entity = $entity->getTranslation($langcode);

        try {
          $result = $generator->createEntityAlias($translated_entity, 'update');
        }
        catch (\InvalidArgumentException $e) {
          $generator->messenger()->addError($e->getMessage());
        }

        if ($result) {
          $updates++;
        }
      }
    }

    if (!empty($options['message'])) {
      $this->messenger->addMessage($this->translationManager
        ->formatPlural(count($ids), 'Updated 1 %label URL alias.', 'Updated @count %label URL aliases.'), [
        '%label' => $this->getLabel(),
      ]);
    }

    return $updates;
  }

}

Et nous y voilà ! À vous les alias finement ciselés pour parfaire votre chef d'œuvre ! Vous pouvez commiter tout cela et retourner prendre des nouvelles de Peter Falk, il est sur le point d'attraper le meurtrier et résoudre l'enquête.


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.