Comment afficher plus de 10 éléments dans la saisie semi-automatique du widget de lien?

10

Il s'agit d'une question sur le module Link. Étant donné qu'avec le module Link, vous pouvez saisir des liens externes ou internes, nous nous appuyons fortement sur lui.

Malheureusement, le nombre d'éléments à afficher à partir de son champ de saisie semi-automatique est limité à 10. Nous avons beaucoup de nœuds avec des titres presque identiques et il arrive donc que le nœud que nous recherchons ne s'affiche pas dans le champ de saisie semi-automatique lorsqu'il y a plus de 10 titres correspondants.

La limite est codée en dur core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Existe-t-il un moyen élégant d'augmenter ce petit nombre à partir d'un module personnalisé? Dois-je prolonger class EntityAutocompleteMatcher? Où devrais-je placer mon extension et comment s'assurer qu'elle est exécutée à partir du widget de lien?

leymannx
la source

Réponses:

10

Si vous pouvez vivre en remplaçant toutes les limites de saisie semi-automatique, vous pouvez remplacer un service principal dans Drupal 8;

Le service que vous devez remplacer est ici dans core.services.yml:

  entity.autocomplete_matcher:
    class: Drupal\Core\Entity\EntityAutocompleteMatcher
    arguments: ['@plugin.manager.entity_reference_selection']

Dans votre module personnalisé, ajoutez une classe qui implémente ServiceModifierInterface

namespace Drupal\mymodule;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

class MyModuleServiceProvider implements ServiceModifierInterface {

  /**
   * Modifies existing service definitions.
   *
   * @param ContainerBuilder $container
   *   The ContainerBuilder whose service definitions can be altered.
   */
  public function alter(ContainerBuilder $container) {

    for ($id = 'entity.autocomplete_matcher'; $container->hasAlias($id); $id = (string) $container->getAlias($id));
    $definition = $container->getDefinition($id);
    $definition->setClass('Drupal\mymodule\Entity\EntityAutocompleteMatcherCustom');
    $container->setDefinition($id, $definition);
  }

}

Copiez ensuite EntityAutocompleteMatcher.php dans votre module à /src/Entity/EntityAutocompleteMatcherCustom.php

Ensuite, mettez à jour le 10 codé en dur à 50, ou quelle que soit la limite que vous souhaitez:

namespace Drupal\mymodule\Entity;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Entity\EntityAutocompleteMatcher;

/**
 * Matcher class to get autocompletion results for entity reference.
 */
class EntityAutocompleteMatcherCustom extends EntityAutocompleteMatcher {

  /*
   * {@inheritdoc]
   */
  public function getMatches($target_type, $selection_handler, $selection_settings, $string = '') {

    $matches = array();

    $options = array(
      'target_type' => $target_type,
      'handler' => $selection_handler,
      'handler_settings' => $selection_settings,
    );
    $handler = $this->selectionManager->getInstance($options);

    if (isset($string)) {
      // Get an array of matching entities.
      $match_operator = !empty($selection_settings['match_operator']) ? $selection_settings['match_operator'] : 'CONTAINS';
      // Changing limit from 10 to 50.
      $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 50);

      // Loop through the entities and convert them into autocomplete output.
      foreach ($entity_labels as $values) {
        foreach ($values as $entity_id => $label) {
          $key = "$label ($entity_id)";
          // Strip things like starting/trailing white spaces, line breaks and
          // tags.
          $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
          // Names containing commas or quotes must be wrapped in quotes.
          $key = Tags::encode($key);
          $matches[] = array('value' => $key, 'label' => $label);
        }
      }
    }

    return $matches;
  }

}

Évidemment, remplacer les services de base comporte certains risques, mais c'est cool que vous puissiez le faire.

Quels sont les risques de remplacer un service principal?

1) Vous pouvez perdre les avantages des mises à jour lorsque vous mettez à jour le noyau. S'il existe un correctif de sécurité critique dans le service et que votre copie modifiée comporte une faille de sécurité, vous ne bénéficierez pas de la mise à jour de ce code par la communauté.

2) Les autres modules que vous installez peuvent avoir des dépendances avec le service d'origine avec son jeu de fonctionnalités d'origine. Disons donc qu'il y a du code dans un autre module qui se cassera si le nombre d'entrées de saisie semi-automatique est supérieur ou inférieur à 10, vous ne le saurez pas, jusqu'à ce qu'il vous affecte.

3) Cela rend votre base de code plus difficile à maintenir. Vous devez vous rappeler que vous n'utilisez pas le noyau Drupal, mais une version étendue. Les autres développeurs qui rejoignent votre projet après votre départ peuvent avoir du mal à comprendre pourquoi un service se comporte de manière non standard.

S'agit-il d'un noyau de piratage?

Cela dépend de la façon dont vous le voyez. Cela ne va pas dans le module de base et ne change pas de code. Il ne s'agit même pas de créer un patch, de l'appliquer et de le suivre avec un gestionnaire de packages tel que composer. Il s'agit davantage d'une personnalisation unique qui modifie le comportement du cœur d'un site, similaire à un crochet ALTER. Il est plus autonome qu'un hack de base, car il se trouve dans votre propre module personnalisé sur votre site. Ainsi, les mises à jour principales du service d'origine ne seront pas affectées, de la même manière que si vous corrigiez ou piratiez le code de service d'origine.

Mais il présente certains des mêmes risques que le noyau de piratage, comme mentionné ci-dessus.

Dans la question d'origine, le problème était que les titres des nœuds n'étaient pas assez uniques. La meilleure solution, autre que la modification globale de la limite lors des listes déroulantes, serait de résoudre le problème de l'unicité.

Ce que je suggérerais, c'est d'ajouter un nouveau champ field_display_title et de l'utiliser sur la page, et si vous en avez besoin, un autre champ field_teaser_title à afficher sur les pages de liste où vous avez besoin d'un titre plus court. Ensuite, le titre réel qui est tiré dans la liste déroulante de sélection de référence d'entité peut être utile à vos éditeurs et être unique, comme «Mon article (page 1)» si le problème est que chaque page a le même titre. Vous n'avez alors pas à remplacer un service principal.

Lorsque vous rencontrez un problème avec Drupal, essayez de trouver la solution qui nécessite le moins de code personnalisé. Cela rend votre site plus stable, plus facile à entretenir et vous fait gagner du temps.

oknate
la source
3
La substitution d'un service de base a les mêmes implications que l'implémentation de hooks ALTER. Les risques ont lieu mais ils sont assez mineurs et peuvent être atténués avec une documentation de code appropriée.
ya.teck
1
Bien sûr, un bon nombre de ces remplacements, crochets, correctifs peuvent réduire la maintenabilité du projet.
ya.teck
Cela me semble être un cas d'utilisation parfait pour un [décorateur de service] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau
7

Je suppose que remplacer EntityAutocompleteMatcher affectera tous les éléments du formulaire de saisie semi-automatique sur votre site. Pour que je crée un nouveau plugin de sélection d'entité à la place, car c'est une approche plus granulaire. Le plugin pourrait être activé par champ. Voici un exemple d'un tel plugin. https://drupal.stackexchange.com/a/220136/433

Dans votre cas, la mise en œuvre serait encore plus triviale:

Fichier: modules / example / src / Plugin / EntityReferenceSelection / ExampleSelection.php

namespace Drupal\example\Plugin\EntityReferenceSelection;

use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;

/**
 * Entity reference selection.
 *
 * @EntityReferenceSelection(
 *   id = "example:node",
 *   label = @Translation("Example node"),
 *   group = "example",
 * )
 */
class ExampleSelection extends NodeSelection {

  /**
   * {@inheritdoc}
   */
  public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
   return parent::getReferenceableEntities($match, $match_operator, 25);
  }

}

L'utilisation de NodeSelection comme classe de base au lieu de DefaultSelection vous permettrait de filtrer les nœuds référencés par leur état. Notez que le référencement d'autres types d'entités n'est pas encore pris en charge .

Contrairement au widget de lien de référence d'entité ne permettant pas de spécifier le plug-in de sélection via l'interface utilisateur, vous devez donc le définir par programme à l'aide de hook_field_widget_WIDGET_TYPE_form_alter () .

/**
 * Implements hook_field_widget_WIDGET_TYPE_form_alter().
 */
function example_field_widget_link_default_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  // Replace default selection handler to increase limit of displayed entities.
  $element['uri']['#selection_handler'] = 'example:node';
}

Il est important que l'ID du plugin contienne un point-virgule.

ya.teck
la source
4

Un autre moyen simple de modifier le nombre de résultats consiste à modifier la valeur de la plage dans la requête:

/**
 * Implements hook_query_TAG_alter() for entity reference selection handlers.
 *
 * We like tho show always 30 results instead of the 10 definied in EntityAutocompleteMatcher::getMatches()
 */
function MODULE_query_entity_reference_alter(AlterableInterface $query) {
  $query->range(0, 30);
}
Weri
la source
1

@Weri, j'éviterais de le faire, après avoir implémenté votre suggestion et passé la meilleure partie de la journée à essayer de résoudre un autre problème.

La modification de requête que vous proposez affecte également la référence d'entrée lors de la liaison des paragraphes aux nœuds. Un nœud sur lequel je me réveillais avait 80+ éléments de paragraphe avant d'ajouter la modification. Une fois ajouté, je n'ai pas pu enregistrer le nœud. Supprimer / commenter l'alter a résolu le problème.

Mise à jour

Envelopper le $ query-> range () dans une vérification d'itinéraire résout le problème pour moi, par exemple,

function mymodule_query_entity_reference_alter($query) {
  $routeMatch = \Drupal::routeMatch();
  if ($routeMatch->getRouteName() == 'system.entity_autocomplete') {
    $query->range(0, 20);
  }
}
Richard
la source
0

FWIW, vous pouvez simplement définir l'affichage du formulaire du champ sur "Sélectionner la liste" au lieu de "Saisie semi-automatique".

Ensuite, vous obtiendrez toutes les options, bien que dans un format moins pratique, mais aucun piratage requis.

ognockocaten
la source