Drupal 8 & Drupal 9 - Surcharger l'autocomplétion d'un champ « entity_reference »

Posté le Jeudi 3 décembre 2020 - 17:14

Il est possible dans drupal 8 et 9 de surcharger l'auto-completion d'un champ « référence à un type d'entité », à la fois la requête générée (pour par exemple faire la recherche sur d'autres champs que le titre, mais aussi le label des éléments retournés.

Attention, la seconde étape diffère entre les versions 8 et 9 de drupal. Logiquement la version drupal 9 fonctionne sur les dernières version de drupal 8, je n'ai pas vérifié. Mais l'inverse ne fonctionne en tout cas pas !

Modification du formulaire (drupal 8 et drupal 9)

Pour cela il faut commencer par altérer le formulaire pour modifier un attribut de notre champ autocomplete :

// mon_module.module (ou bien un fichier de définition de formulaire directement

// ici, mon champ « entity_reference » est issue
// dashboard:issue est une clé qui sera définie à l'étape 2


function mon_module_form_alter(&$form, FormStateInterface $form_state) {
  if ($form['id'] === '...') {
    $form['issue']['widget'][0]['target_id']['#selection_handler'] = 'dashboard:issue';
  }
}

Définition du nouveau filtre de sélection  (drupal 9)

mon_module/src/Plugin/EntityReferenceSelection/IssueSelection.php

<?php

namespace Drupal\mon_module\Plugin\EntityReferenceSelection;

use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;

/**
 * Surcharge de l'autocomplete pour les tickets
 *
 * @EntityReferenceSelection(
 *   id = "dashboard:issue", // on retrouve ici la clé définie à l'étape une
 *   label = @Translation("Selection de ticket"),
 *   entity_types = {"issue"}, // définir ici le type d'entité que l'on veut gérer
 *   group = "default",
 *   weight = 1
 * )
 */
class IssueSelection extends DefaultSelection {

  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
    $target_type = $this->getConfiguration()['target_type'];
    
    // ici c'est une EntityQuery classique
    $query = $this->buildEntityQuery($match, $match_operator);
    $or = $query->orConditionGroup();

    // Je fais une condition sur le titre de mon contenu mais aussi sur le champ « external_id »
    $or->condition('title', $match, $match_operator);
    $or->condition('external_id', $match, $match_operator);
    $query->condition($or);

    if ($limit > 0) {
      $query->range(0, $limit);
    }
    $result = $query->execute();

    if (empty($result)) {
      return [];
    }

    $options = [];
    /** @var \Drupal\dashboard\Entity\Issue[] $entities */
    $entities = $this->entityTypeManager->getStorage($target_type)->loadMultiple($result);
    foreach ($entities as $entity_id => $entity) {
      $project = $entity->getProject();
      $bundle = $entity->bundle();
      
      // Ici, pour chaque résultat, définition du label de l'élément qui sera affiché dans la liste déroulante
      $options[$bundle][$entity_id] = Html::escape($project->label() . ' #' . $entity->getExternalId(). ' -  ' . $entity->label() );
    }

    return $options;
  }

}

Définition du nouveau filtre de sélection  (drupal 8)

mon_module/src/Plugin/EntityReferenceSelection/IssueSelection.php

<?php

namespace Drupal\mon_module\Plugin\EntityReferenceSelection;

use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;

/**
 * Surcharge de l'autocomplete pour les tickets
 *
 * @EntityReferenceSelection(
 *   id = "dashboard:issue", // on retrouve ici la clé définie à l'étape une
 *   label = @Translation("Selection de ticket"),
 *   entity_types = {"issue"}, // définir ici le type d'entité que l'on veut gérer
 *   group = "default",
 *   weight = 1
 * )
 */
class IssueSelection extends DefaultSelection {

  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
    $target_type = $this->getConfiguration()['target_type'];
    
    // ici c'est une EntityQuery classique
    $query = $this->entityManager->getStorage($target_type)->getQuery();
    $or = $query->orConditionGroup();

    // Je fais une condition sur le titre de mon contenu mais aussi sur le champ « external_id »
    $or->condition('title', $match, $match_operator);
    $or->condition('external_id', $match, $match_operator);
    $query->condition($or);

    if ($limit > 0) {
      $query->range(0, $limit);
    }
    $result = $query->execute();

    if (empty($result)) {
      return [];
    }

    $options = [];
    /** @var \Drupal\dashboard\Entity\Issue[] $entities */
    $entities = $this->entityManager->getStorage($target_type)->loadMultiple($result);
    foreach ($entities as $entity_id => $entity) {
      $project = $entity->getProject();
      $bundle = $entity->bundle();
      
      // Ici, pour chaque résultat, définition du label de l'élément qui sera affiché dans la liste déroulante
      $options[$bundle][$entity_id] = Html::escape($project->label() . ' #' . $entity->getExternalId(). ' -  ' . $entity->label() );
    }

    return $options;
  }

}

 

Aller plus loin ?

 

Commentaires

Salut Kevin,
Merci pour ce tuto pour customiser la recherche une entité et pour afficher un label plus adapté à celui proposé par défaut. Mais serait tu comment modifier aussi le label une entité déjà référencée par le formulaire, pour avoir la même mise en forme de label affiché par l'autocomplétion modifiée par ton code par exemple "Nom du projet - #xx - Label du ticket" au lieu de "Label du ticket (xx)" ?

Ah tiens je n'avais même pas remarqué que ça n'était pas pris en compte :)

Il faudrait que je creuse ça !

Ajouter un commentaire

Ne sera pas publié

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