Jak wyświetlić więcej niż 10 elementów w autouzupełnianiu widgetów linków?

10

To jest pytanie dotyczące modułu Link. Ponieważ w module Link możesz wprowadzać zarówno zewnętrzne, jak i wewnętrzne łącza, mocno na nim polegamy.

Niestety liczba elementów, które mają być wyświetlane z pola autouzupełniania jest ograniczona do 10. Mamy wiele węzłów o prawie identycznych tytułach, więc okazuje się, że szukany węzeł nie wyświetla się w polu autouzupełniania, gdy są ponad 10 pasujących tytułów.

Limit jest zakodowany na stałe core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Czy istnieje elegancki sposób na zwiększenie tej małej liczby z niestandardowego modułu? Czy muszę przedłużyć class EntityAutocompleteMatcher? Gdzie musiałbym umieścić moje rozszerzenie i jak upewnić się, że zostanie ono wykonane z poziomu widgetu łącza?

leymannx
źródło

Odpowiedzi:

10

Jeśli możesz żyć z nadpisywaniem wszystkich limitów autouzupełniania, możesz zastąpić usługę podstawową w Drupal 8;

Usługa, którą należy zastąpić, znajduje się tutaj w pliku core.services.yml:

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

W niestandardowym module dodaj klasę, która implementuje 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);
  }

}

Następnie skopiuj EntityAutocompleteMatcher.php do swojego modułu na /src/Entity/EntityAutocompleteMatcherCustom.php

Następnie zaktualizuj zakodowane 10 na 50 lub dowolny limit, jaki chcesz:

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

}

Oczywiście zastąpienie podstawowych usług wiąże się z pewnym ryzykiem, ale fajnie jest to zrobić.

Jakie jest ryzyko zastąpienia usługi podstawowej?

1) Możesz stracić zalety aktualizacji podczas aktualizacji rdzenia. Jeśli w usłudze jest krytyczna poprawka bezpieczeństwa, a zmieniona kopia ma dziurę w zabezpieczeniach, społeczność nie zaktualizuje tego kodu.

2) Inne instalowane moduły mogą zależeć od oryginalnej usługi z jej oryginalnym zestawem funkcji. Powiedzmy więc, że jest jakiś kod w innym module, który się zepsuje, jeśli liczba pozycji autouzupełniania będzie większa lub mniejsza niż 10, nie będziesz o tym wiedział, dopóki nie wpłynie to na ciebie.

3) Utrudnia utrzymanie bazy kodu. Musisz pamiętać, że nie używasz podstawowego Drupala, ale jego rozszerzoną wersję. Inni programiści, którzy dołączają do twojego projektu po twoim odejściu, mogą mieć trudności z ustaleniem, dlaczego usługa zachowuje się w niestandardowy sposób.

Czy to rdzeń hakerski?

Zależy jak na to spojrzeć. Nie wchodzi do modułu podstawowego i nie zmienia kodu. Nie tworzy nawet łatki, nie stosuje jej i nie śledzi za pomocą menedżera pakietów, takiego jak kompozytor. Jest to bardziej jednorazowe dostosowanie, które zmienia podstawowe zachowanie witryny, podobnie jak hak ALTER. Jest to bardziej samowystarczalne niż hack rdzenia, ponieważ znajduje się w twoim własnym niestandardowym module na twojej stronie. Tak więc nie będzie to miało wpływu na podstawowe aktualizacje oryginalnej usługi, tak jak w przypadku łatania lub zhakowania oryginalnego kodu usługi.

Ale wiąże się to z takimi samymi zagrożeniami jak hakowanie rdzenia, jak wspomniano powyżej.

W pierwotnym pytaniu problem polegał na tym, że tytuły węzłów nie są wystarczająco unikalne. Lepszym rozwiązaniem, poza globalną zmianą limitu list rozwijanych, byłoby rozwiązanie problemu wyjątkowości.

Sugeruję dodanie nowego pola field_display_title i użycie go na stronie, a jeśli będzie to potrzebne, kolejne pole field_teaser_title do wyświetlenia na stronach list, na których potrzebujesz krótszego tytułu. Wówczas faktyczny tytuł, który zostanie wciągnięty do menu wyboru odniesienia do encji, może być przydatny dla twoich redaktorów i być unikalny, taki jak „Mój artykuł (strona 1)”, jeśli problem polega na tym, że każda strona ma ten sam tytuł. Wtedy nie musisz zastępować usługi podstawowej.

Gdy napotkasz problem z Drupalem, spróbuj znaleźć rozwiązanie, które wymaga najmniejszej ilości niestandardowego kodu. Dzięki temu witryna jest bardziej stabilna, łatwiejsza w utrzymaniu i oszczędza czas.

oknate
źródło
3
Zasadniczo zastąpienie usługi podstawowej ma takie same implikacje, jak implementacja przechwytywania ALTER. Ryzyko ma miejsce, ale są niewielkie i można je złagodzić za pomocą odpowiedniej dokumentacji kodu.
ya.teck
1
Z tego powodu duża liczba takich nadpisań, haków, łatek może zmniejszyć możliwość utrzymania projektu.
ya.teck
Wydaje mi się, że jest to idealny przypadek użycia [dekoratora usług] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau
7

Podejrzewam, że zastąpienie EntityAutocompleteMatcher wpłynie na wszystkie elementy formularza autouzupełniania w Twojej witrynie. Aby zamiast tego utworzyć nową wtyczkę wyboru encji, ponieważ jest to bardziej szczegółowe podejście. Wtyczkę można włączyć dla każdego pola. Oto przykład takiej wtyczki. https://drupal.stackexchange.com/a/220136/433

W twoim przypadku wdrożenie byłoby jeszcze bardziej trywialne:

Plik: moduły / przykład / 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);
  }

}

Użycie NodeSelection jako klasy bazowej zamiast DefaultSelection umożliwiłoby filtrowanie węzłów, do których istnieją odniesienia, według ich statusu. Pamiętaj, że odwoływanie się do innych typów jednostek nie jest jeszcze obsługiwane .

W odróżnieniu od widżetu odnośnika do encji nie można określić wtyczki wyboru poprzez interfejs użytkownika, dlatego należy ustawić ją programowo za pomocą 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';
}

Ważne jest, aby identyfikator wtyczki zawierał średnik.

ya.teck
źródło
4

Innym łatwym sposobem na modyfikację liczby wyników jest zmiana wartości zakresu w zapytaniu:

/**
 * 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
źródło
1

@Weri, unikałbym tego, po prostu wdrożyłem twoją sugestię i spędziłem większość dnia próbując rozwiązać inny problem.

Sugerowana zmiana zapytania wpływa również na odwołanie do zapytania podczas łączenia akapitów z węzłami. Węzeł, na którym się obudziłem, miał ponad 80 akapitowy element, zanim dodałem alter. Po dodaniu nie mogłem zapisać węzła. Usunięcie / skomentowanie zmiany rozwiązało problem.

Aktualizacja

Zawijanie $ query-> range () podczas sprawdzania trasy rozwiązuje problem, na przykład:

function mymodule_query_entity_reference_alter($query) {
  $routeMatch = \Drupal::routeMatch();
  if ($routeMatch->getRouteName() == 'system.entity_autocomplete') {
    $query->range(0, 20);
  }
}
Richard
źródło
0

FWIW, możesz po prostu ustawić wyświetlanie pola pola na „Wybierz listę” zamiast „Autouzupełniania”.

Następnie otrzymasz wszystkie opcje, aczkolwiek w mniej wygodnym formacie, ale nie wymaga to włamań.

ognockocaten
źródło