Wie werden mehr als 10 Elemente in der automatischen Vervollständigung des Link-Widgets angezeigt?

10

Dies ist eine Frage zum Link-Modul. Da Sie mit dem Link-Modul sowohl externe als auch interne Links eingeben können, verlassen wir uns stark darauf.

Leider ist die Anzahl der Elemente, die in seinem Feld für die automatische Vervollständigung angezeigt werden sollen, auf 10 begrenzt. Wir haben viele Knoten mit nahezu identischen Titeln, sodass der gesuchte Knoten nicht im Feld für die automatische Vervollständigung angezeigt wird, wenn es solche gibt mehr als 10 passende Titel.

Das Limit ist fest codiert core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Gibt es eine elegante Möglichkeit, diese kleine Zahl innerhalb eines benutzerdefinierten Moduls zu erhöhen? Muss ich verlängern class EntityAutocompleteMatcher? Wo müsste ich meine Erweiterung platzieren und wie kann ich sicherstellen, dass sie im Link-Widget ausgeführt wird?

Leymannx
quelle

Antworten:

3

Es sieht so aus, als ob der neuesten Version von Core, 8.8+, eine ordnungsgemäße Korrektur hinzugefügt wurde (siehe # 2863188) .

Geben Sie hier die Bildbeschreibung ein

Richard
quelle
10

Wenn Sie damit leben können, alle Grenzwerte für die automatische Vervollständigung zu überschreiben, können Sie einen Kerndienst in Drupal 8 überschreiben .

Der Dienst, den Sie überschreiben müssen, befindet sich hier in core.services.yml:

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

Fügen Sie in Ihrem benutzerdefinierten Modul eine Klasse hinzu, die ServiceModifierInterface implementiert

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

}

Kopieren Sie dann EntityAutocompleteMatcher.php in Ihr Modul unter /src/Entity/EntityAutocompleteMatcherCustom.php

Aktualisieren Sie dann die fest codierte 10 auf 50 oder ein beliebiges Limit, das Sie möchten:

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

}

Das Überschreiben von Kerndiensten birgt natürlich einige Risiken, aber es ist cool, dass Sie dies tun können.

Was sind die Risiken beim Überschreiben eines Kerndienstes?

1) Sie können die Vorteile von Updates verlieren, wenn Sie den Core aktualisieren. Wenn der Dienst eine kritische Sicherheitslücke enthält und Ihre geänderte Kopie die Sicherheitslücke aufweist, können Sie nicht davon profitieren, dass die Community diesen Code aktualisiert.

2) Andere Module, die Sie installieren, sind möglicherweise vom ursprünglichen Dienst mit seinem ursprünglichen Funktionsumfang abhängig. Nehmen wir also an, in einem anderen Modul befindet sich Code, der beschädigt wird, wenn die Anzahl der Einträge für die automatische Vervollständigung größer oder kleiner als 10 ist. Sie werden nichts davon wissen, bis Sie davon betroffen sind.

3) Es macht es schwieriger, Ihre Codebasis zu pflegen. Sie müssen sich daran erinnern, dass Sie nicht Core Drupal verwenden, sondern eine erweiterte Version. Andere Entwickler, die sich Ihrem Projekt anschließen, nachdem Sie das Projekt verlassen haben, haben möglicherweise Schwierigkeiten herauszufinden, warum sich ein Dienst nicht standardmäßig verhält.

Ist das Hacking Core?

Kommt darauf an, wie du es betrachtest. Es geht nicht in das Kernmodul und ändert den Code. Es geht nicht einmal darum, einen Patch zu erstellen, ihn anzuwenden und mit einem Paketmanager wie Composer zu verfolgen. Es handelt sich eher um eine einmalige Anpassung, die das Kernverhalten einer Site ändert, ähnlich einem ALTER-Hook. Es ist eigenständiger als ein Core-Hack, da es sich in Ihrem eigenen benutzerdefinierten Modul auf Ihrer Site befindet. Kernaktualisierungen des ursprünglichen Dienstes sind also nicht betroffen, genauso wie wenn Sie den ursprünglichen Dienstcode gepatcht oder gehackt haben.

Aber es hat einige der gleichen Risiken wie Hacking Core, wie oben erwähnt.

In der ursprünglichen Frage war das Problem, dass die Knotentitel nicht eindeutig genug sind. Die bessere Lösung als die globale Änderung des Grenzwerts für Dropdowns wäre die Lösung des Eindeutigkeitsproblems.

Was ich vorschlagen würde, ist, ein neues Feld field_display_title hinzuzufügen und dieses auf der Seite zu verwenden, und wenn Sie es benötigen, ein anderes Feld field_teaser_title für die Anzeige auf Listenseiten, auf denen Sie einen kürzeren Titel benötigen. Dann kann der tatsächliche Titel, der in die Dropdown-Liste zur Auswahl von Entitätsreferenzen gezogen wird, für Ihre Redakteure nützlich und eindeutig sein, z. B. "Mein Artikel (Seite 1)", wenn das Problem darin besteht, dass jede Seite denselben Titel hat. Dann müssen Sie einen Kerndienst nicht überschreiben.

Wenn Sie auf ein Problem mit Drupal stoßen, versuchen Sie, die Lösung zu finden, die die geringste Menge an benutzerdefiniertem Code erfordert. Dies macht Ihre Site stabiler, einfacher zu warten und spart Ihnen Zeit.

oknate
quelle
3
Grundsätzlich hat das Überschreiben eines Kerndienstes dieselben Auswirkungen wie das Implementieren von ALTER-Hooks. Die Risiken treten auf, sind jedoch recht gering und können durch eine ordnungsgemäße Codedokumentation gemindert werden.
ya.teck
1
Natürlich kann eine gute Anzahl solcher Überschreibungen, Hooks und Patches die Wartbarkeit des Projekts beeinträchtigen.
ya.teck
Dies scheint mir ein perfekter Anwendungsfall für einen [Service-Dekorateur] zu sein ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau
7

Ich nehme an, dass das Überschreiben von EntityAutocompleteMatcher alle Formularelemente für die automatische Vervollständigung auf Ihrer Site beeinflusst. Damit ich stattdessen ein neues Entity-Auswahl-Plugin erstellen würde, da es detaillierter ist. Das Plugin kann pro Feld aktiviert werden. Hier ist ein Beispiel für ein solches Plugin. /drupal//a/220136/433

In Ihrem Fall wäre die Implementierung noch trivialer:

Datei: 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);
  }

}

Wenn Sie NodeSelection als Basisklasse anstelle von DefaultSelection verwenden, können Sie referenzierte Knoten nach ihrem Status filtern. Beachten Sie, dass das Verweisen auf andere Entitätstypen noch nicht unterstützt wird .

Im Gegensatz zum Entitätsreferenz-Link-Widget kann das Auswahl-Plugin nicht über die Benutzeroberfläche angegeben werden. Daher müssen Sie es programmgesteuert mit hook_field_widget_WIDGET_TYPE_form_alter () festlegen .

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

Es ist wichtig, dass die Plugin-ID ein Semikolon enthält.

ya.teck
quelle
4

Eine andere einfache Möglichkeit, die Anzahl der Ergebnisse zu ändern, besteht darin, den Bereichswert in der Abfrage zu ändern:

/**
 * 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
quelle
1

@Weri, ich würde das vermeiden, da ich gerade Ihren Vorschlag umgesetzt und den größten Teil eines Tages damit verbracht habe, ein anderes Problem zu beheben.

Die von Ihnen vorgeschlagene Abfrageänderung wirkt sich auch auf die Gesamtreferenz aus, wenn Absätze mit Knoten verknüpft werden. Ein Knoten, auf dem ich aufwachte, hatte mehr als 80 Absätze, bevor ich die Änderung hinzufügte. Nach dem Hinzufügen konnte ich den Knoten nicht speichern. Durch Entfernen / Auskommentieren der Änderung wurde das Problem behoben.

Aktualisieren

Das Umschließen von $ query-> range () in eine Routenprüfung behebt das Problem für mich, z.

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

FWIW, Sie können einfach die Formularanzeige des Feldes auf "Liste auswählen" anstelle von "Autocomplete" setzen.

Dann erhalten Sie alle Optionen, allerdings in einem weniger praktischen Format, aber ohne Hacks.

ognockocaten
quelle