如何在链接窗口小部件自动完成中显示10个以上的项目?


10

这是有关链接模块的问题。由于使用链接模块可以同时输入外部或内部链接,因此我们强烈依赖它。

不幸的是,要从其“自动完成”字段中显示的项目数限制为10个。我们有许多节点的标题几乎相同,因此,当存在以下情况时,我们正在搜索的节点不会显示在“自动完成”字段中超过10个匹配的标题。

该限制已硬编码在中core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php。有没有一种优雅的方法可以从自定义模块中增加这个数量呢?我需要扩展class EntityAutocompleteMatcher吗?我必须在哪里放置扩展,以及如何确保从链接小部件中执行扩展?

Answers:



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挂钩。它比核心hack更独立,因为它位于站点上您自己的自定义模块中。因此,对原始服务的核心更新不会受到影响,就像您修补或破解原始服务代码一样。

但是,如上所述,它确实具有与黑客核心相同的风险。

在最初的问题中,问题在于节点标题不够唯一。除了全局更改下拉菜单的限制之外,更好的解决方案是解决唯一性问题。

我建议添加一个新字段field_display_title并在页面上使用它,如果需要,则另一个字段field_teaser_title用于显示在需要较短标题的列表页面上。然后,如果问题是每个页面具有相同的标题,则被拉入“实体引用选择”下拉列表中的实际标题可能对您的编辑者有用并且是唯一的,例如“我的文章(第1页)”。然后,您不必重写核心服务。

当您遇到Drupal的问题时,请尝试查找需要最少数量的自定义代码的解决方案。这使您的网站更稳定,更易于维护并节省时间。


3
基本上,覆盖核心服务与实现ALTER挂钩具有相同的含义。发生了风险,但是风险很小,可以通过适当的代码文档来减轻风险。
ya.teck

1
当然,大量此类覆盖,挂钩,补丁可能会降低项目的可维护性。
ya.teck

在我看来,这似乎是[服务装饰器](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';
}

插件ID包含分号非常重要。


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,我会避免这样做,只是实现了您的建议,并花了一天的大部分时间尝试解决另一个问题。

您建议的查询alter在将段落链接到节点时也会影响整个引用。在添加更改之前,我正在唤醒的节点具有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,您只需将字段的表单显示设置为“选择列表”,而不是“自动完成”即可。

然后,您将获得所有选项,尽管格式较不方便,但无需黑客。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.