Как отобразить более 10 элементов в автозаполнении виджета ссылок?


10

Это вопрос о модуле Link. Поскольку с модулем Link вы можете вводить как внешние, так и внутренние ссылки, мы настоятельно на это полагаемся.

К сожалению, количество элементов, отображаемых в поле автозаполнения, ограничено 10. У нас много узлов с почти одинаковыми заголовками, и поэтому искомый узел не отображается в поле автозаполнения при наличии более 10 подходящих названий.

Ограничение жестко закодировано в core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Есть ли элегантный способ увеличить это небольшое число из пользовательского модуля? Должен ли я продлить class EntityAutocompleteMatcher? Где мне разместить свое расширение и как обеспечить его выполнение из виджета ссылки?

Ответы:



10

Если вы можете переопределить все ограничения автозаполнения, вы можете переопределить основную службу в Drupal 8;

Служба, которую вы должны переопределить, находится здесь в core.services.yml:

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

В свой пользовательский модуль добавьте класс, который реализует 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);
  }

}

Затем скопируйте EntityAutocompleteMatcher.php в свой модуль по адресу /src/Entity/EntityAutocompleteMatcherCustom.php

Затем измените жестко запрограммированное 10 на 50 или любое другое ограничение:

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

}

Очевидно, что переопределение основных служб сопряжено с некоторыми рисками, но это здорово, что вы можете сделать это.

Каковы риски переопределения основного сервиса?

1) Вы можете потерять преимущества обновлений при обновлении ядра. Если в службе есть критическое исправление безопасности, и ваша измененная копия имеет дыру в безопасности, вы не получите пользы от обновления этого кода сообществом.

2) Другие модули, которые вы устанавливаете, могут иметь зависимости от исходного сервиса с его исходным набором функций. Итак, допустим, что в другом модуле есть какой-то код, который сломается, если число записей автозаполнения больше или меньше 10, вы не будете знать об этом, пока это не повлияет на вас.

3) Это делает вашу кодовую базу труднее поддерживать. Вы должны помнить, что вы используете не ядро ​​Drupal, а расширенную версию. Другим разработчикам, которые присоединяются к вашему проекту после вашего ухода, может быть трудно понять, почему сервис ведет себя нестандартным образом.

Это хакерское ядро?

Зависит от того, как ты смотришь на это. Это не входит в основной модуль и не меняет код. Это даже не создание патча, его применение и отслеживание с помощью менеджера пакетов, такого как composer. Это больше одноразовая настройка, которая изменяет поведение ядра сайта, аналогично перехвату ALTER. Он более автономен, чем основной хак, потому что он находится внутри вашего собственного пользовательского модуля на вашем сайте. Таким образом, основные обновления оригинальной службы не будут затронуты так же, как если бы вы исправили или взломали исходный код службы.

Но он имеет некоторые из тех же рисков, что и хакерское ядро, как упомянуто выше.

В первоначальном вопросе проблема заключалась в том, что названия узлов не были достаточно уникальными. Лучшим решением, кроме глобального изменения ограничения на выпадающие списки, было бы решение проблемы уникальности.

Я бы предложил добавить новое поле field_display_title и использовать его на странице, а при необходимости другое поле field_teaser_title для отображения на страницах списка, где вам нужен более короткий заголовок. Тогда фактический заголовок, который добавляется в раскрывающийся список выбора ссылки на сущность, может быть полезен для ваших редакторов и быть уникальным, например «Моя статья (страница 1)», если проблема заключается в том, что каждая страница имеет одинаковый заголовок. Тогда вам не нужно переопределять основной сервис.

Когда вы сталкиваетесь с проблемой с Drupal, попробуйте найти решение, которое требует наименьшего количества пользовательского кода. Это делает ваш сайт более стабильным, простым в обслуживании и экономит ваше время.


3
В основном, переопределение базовой службы имеет те же последствия, что и реализация хитов ALTER. Риски имеют место, но они довольно незначительны и могут быть уменьшены с помощью надлежащей документации кода.
я.тек

1
Конечно, большое количество таких переопределений, хуков, патчей может снизить удобство сопровождения проекта.
я.тек

Это кажется мне идеальным вариантом использования для [декоратора службы] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Бо

7

Я предполагаю, что переопределение EntityAutocompleteMatcher повлияет на все элементы автозаполнения формы на вашем сайте. Так что вместо этого я бы создал новый плагин выбора сущностей, потому что это более детальный подход. Плагин может быть включен для каждого поля. Вот пример такого плагина. https://drupal.stackexchange.com/a/220136/433

В вашем случае реализация будет еще более тривиальной:

Файл: 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);
  }

}

Использование NodeSelection в качестве базового класса вместо DefaultSelection позволит вам фильтровать ссылочные узлы по их статусу. Обратите внимание, что ссылки на другие типы объектов еще не поддерживаются .

В отличие от виджета ссылка на сущность не позволяет указывать плагин выбора через пользовательский интерфейс, поэтому вам нужно установить его программно, используя 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';
}

Важно, чтобы идентификатор плагина содержал точку с запятой.


4

Другой простой способ изменить количество результатов - изменить значение диапазона в запросе:

/**
 * 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, я бы не стал этого делать, просто реализовав ваше предложение и потратив большую часть дня, пытаясь решить другую проблему.

Предложенный вами запрос на изменение также влияет на начальную ссылку при связывании абзацев с узлами. У узла, на котором я проснулся, было более 80 пунктов абзаца, прежде чем я добавил изменение. После добавления я не смог сохранить узел. Удаление / комментирование измененного исправило проблему.

Обновить

Обертывание $ query-> range () в проверке маршрута устраняет проблему для меня, например,

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

0

FWIW, вы можете просто установить отображение формы поля на «Выбрать список» вместо «Автозаполнение».

Затем вы получите все опции, хотя и в менее удобном формате, но никаких взломов не требуется.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.