Come visualizzare più di 10 elementi nel completamento automatico del widget link?


10

Questa è una domanda sul modulo Link. Poiché con il modulo Link è possibile inserire entrambi i collegamenti esterni o interni, ci affidiamo fortemente a esso.

Sfortunatamente il numero di elementi da visualizzare dal suo campo di completamento automatico è limitato a 10. Abbiamo molti nodi con titoli quasi identici e quindi il nodo che stiamo cercando non viene visualizzato nel campo di completamento automatico quando ci sono più di 10 titoli corrispondenti.

Il limite è codificato core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Esiste un modo elegante per aumentare questo piccolo numero all'interno di un modulo personalizzato? Devo estendere class EntityAutocompleteMatcher? Dove dovrei posizionare la mia estensione e come assicurarmi che venga eseguita all'interno del widget link?

Risposte:



10

Se riesci a superare tutti i limiti di completamento automatico, puoi eseguire l' override di un servizio principale in Drupal 8;

Il servizio che devi sovrascrivere è qui in core.services.yml:

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

Nel modulo personalizzato, aggiungi una classe che implementa 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);
  }

}

Quindi copia EntityAutocompleteMatcher.php nel tuo modulo su /src/Entity/EntityAutocompleteMatcherCustom.php

Quindi aggiorna il 10 hardcoded a 50 o qualsiasi limite desideri:

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;
  }

}

Ovviamente ignorare i servizi di base comporta alcuni rischi, ma è bello poterlo fare.

Quali sono i rischi di ignorare un servizio principale?

1) Potresti perdere i vantaggi degli aggiornamenti quando aggiorni il core. Se nel servizio è presente una correzione di sicurezza critica e la copia modificata presenta un buco di sicurezza, non si trarrà vantaggio dall'aggiornamento della comunità da parte del codice.

2) Altri moduli installati possono avere dipendenze dal servizio originale con il set di funzionalità originale. Quindi supponiamo che ci sia del codice in un altro modulo che si interromperà se il numero di voci di completamento automatico è maggiore o minore di 10, non lo saprai, fino a quando non ti influenza.

3) Rende il tuo codebase più difficile da mantenere. Devi ricordare che non stai usando Drupal core, ma una versione estesa. Altri sviluppatori che si uniscono al tuo progetto dopo la tua partenza potrebbero avere difficoltà a capire perché un servizio si sta comportando in modo non standard.

Questo nucleo di hacking?

Dipende dal tuo punto di vista. Non entra nel modulo principale e non cambia il codice. Non sta nemmeno creando una patch, applicandola e monitorandola con un gestore di pacchetti come compositore. È più di una personalizzazione unica che altera il comportamento di base di un sito, simile a un hook ALTER. È più autonomo di un hack principale, perché è all'interno del tuo modulo personalizzato sul tuo sito. Pertanto, gli aggiornamenti di base al servizio originale non saranno interessati, allo stesso modo come se si correggesse o violasse il codice del servizio originale.

Ma presenta alcuni degli stessi rischi dell'hacking core, come menzionato sopra.

Nella domanda originale, il problema era che i titoli dei nodi non erano abbastanza unici. La soluzione migliore, oltre a modificare il limite a livello globale sui menu a discesa, sarebbe risolvere il problema dell'unicità.

Quello che suggerirei è di aggiungere un nuovo campo field_display_title e usarlo sulla pagina, e se ne hai bisogno un altro campo field_teaser_title per la visualizzazione su pagine di elenco in cui hai bisogno di un titolo più breve. Quindi il titolo effettivo che viene inserito nel menu a discesa Seleziona riferimento entità può essere utile per i tuoi editor ed essere unico, come "Il mio articolo (pagina 1)" se il problema è che ogni pagina ha lo stesso titolo. Quindi non è necessario eseguire l'override di un servizio principale.

Quando riscontri un problema con Drupal, prova a trovare la soluzione che richiede la minima quantità di codice personalizzato. Questo rende il tuo sito più stabile, più facile da mantenere e ti fa risparmiare tempo.


3
Fondamentalmente l'override di un servizio principale ha le stesse implicazioni dell'implementazione degli hook ALTER. I rischi si verificano ma sono piuttosto lievi e possono essere mitigati con un'adeguata documentazione del codice.
ya.teck

1
Di conseguenza, un buon numero di tali sostituzioni, hook, patch può ridurre la manutenibilità del progetto.
ya.teck

Questo mi sembra un perfetto caso d'uso per un [decoratore di servizi] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau

7

Suppongo che l'override di EntityAutocompleteMatcher influirà su tutti gli elementi del modulo di completamento automatico sul tuo sito. In modo da creare un nuovo plug-in di selezione entità invece perché è un approccio più granulare. Il plug-in potrebbe essere abilitato per campo. Ecco un esempio di tale plugin. https://drupal.stackexchange.com/a/220136/433

Nel tuo caso l'implementazione sarebbe ancora più banale:

File: 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'utilizzo di NodeSelection come classe base anziché DefaultSelection consente di filtrare i nodi di riferimento in base al loro stato. Si noti che il riferimento ad altri tipi di entità non è ancora supportato .

A differenza del widget collegamento di riferimento entità non consente di specificare il plug-in di selezione tramite l'interfaccia utente, pertanto è necessario impostarlo a livello di codice utilizzando 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';
}

È importante che l'ID plug-in contenga un punto e virgola.


4

Un altro modo semplice per modificare il numero di risultati è quello di modificare il valore dell'intervallo nella query:

/**
 * 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);
}

1

@Weri, eviterei di farlo, avendo appena implementato il tuo suggerimento e trascorso la maggior parte della giornata cercando di risolvere un altro problema.

La modifica della query suggerita influisce anche sul riferimento entiry quando si collegano i paragrafi ai nodi. Un nodo su cui mi ero svegliato aveva più di 80 articoli di paragrafo prima di aggiungere la modifica. Una volta aggiunto, non sono stato in grado di salvare il nodo. Rimuovere / commentare l'alter ha risolto il problema.

Aggiornare

Avvolgere $ query-> range () in un controllo del percorso risolve il problema per me, ad esempio,

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

0

FWIW, puoi semplicemente impostare la visualizzazione del modulo del campo su "Seleziona elenco" anziché "Completamento automatico".

Quindi, otterrai tutte le opzioni, anche se in un formato meno conveniente, ma non sono necessari hack.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.