Como exibir mais de 10 itens no preenchimento automático do widget de link?

10

Esta é uma pergunta sobre o módulo Link. Como com o módulo Link você pode inserir links externos ou internos, contamos fortemente com ele.

Infelizmente, o número de itens a serem exibidos em seu campo de preenchimento automático é limitado a 10. Temos muitos nós com títulos quase idênticos e, portanto, o nó que estamos procurando não é exibido no campo de preenchimento automático quando há mais de 10 títulos correspondentes.

O limite está codificado core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. Existe uma maneira elegante de aumentar esse pequeno número de dentro de um módulo personalizado? Eu tenho que estender class EntityAutocompleteMatcher? Onde eu teria que colocar minha extensão e como garantir que ela seja executada no widget de link?

leymannx
fonte

Respostas:

10

Se você pode substituir todos os limites de preenchimento automático, pode substituir um serviço principal no Drupal 8;

O serviço que você precisa substituir está aqui em core.services.yml:

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

No seu módulo personalizado, adicione uma classe que implemente 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);
  }

}

Em seguida, copie EntityAutocompleteMatcher.php no seu módulo em /src/Entity/EntityAutocompleteMatcherCustom.php

Em seguida, atualize os 10 codificados para 50, ou o limite que desejar:

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

}

Obviamente, substituir os serviços principais tem alguns riscos, mas é legal que você possa fazer isso.

Quais são os riscos de substituir um serviço principal?

1) Você pode perder os benefícios das atualizações ao atualizar o núcleo. Se houver uma correção crítica de segurança no serviço e sua cópia alterada tiver uma falha de segurança, você não se beneficiará com a atualização da comunidade por esse código.

2) Outros módulos instalados podem ter dependências para o serviço original com seu conjunto de recursos original. Então, digamos que exista algum código em outro módulo que será interrompido se o número de entradas de preenchimento automático for maior ou menor que 10, você não saberá disso até que isso o afete.

3) Torna sua base de código mais difícil de manter. Você deve se lembrar que não está usando o Drupal principal, mas uma versão estendida. Outros desenvolvedores que ingressam no seu projeto depois que você saiu podem ter dificuldade em descobrir por que um serviço está se comportando de maneira não-padrão.

Esse é o núcleo do hacking?

Depende de como você olha para isso. Não está entrando no módulo principal e mudando o código. Não é nem mesmo criar um patch e aplicá-lo e rastreá-lo com um gerenciador de pacotes, como o compositor. É mais uma personalização única que altera o comportamento principal de um site, semelhante a um gancho ALTER. É mais independente do que um hack principal, porque está dentro do seu próprio módulo personalizado em seu site. Portanto, as atualizações principais do serviço original não serão afetadas, da mesma forma como se você corrigisse ou hackeasse o código do serviço original.

Mas tem alguns dos mesmos riscos que o núcleo de hackers, como mencionado acima.

Na pergunta original, o problema era que os títulos dos nós não são exclusivos o suficiente. A melhor solução, além de alterar o limite globalmente nos drop-downs, seria resolver o problema de exclusividade.

O que eu sugeriria é adicionar um novo campo field_display_title e usá-lo na página, e se você precisar dele outro campo field_teaser_title para exibição nas páginas da lista em que você precisa de um título mais curto. Em seguida, o título real que é puxado para a lista suspensa de seleção de referência de entidade pode ser útil para seus editores e ser exclusivo, como "Meu artigo (página 1)" se o problema é que cada página tem o mesmo título. Então você não precisa substituir um serviço principal.

Quando você se deparar com um problema com o Drupal, tente encontrar a solução que requer a menor quantidade de código personalizado. Isso torna seu site mais estável, mais fácil de manter e economiza seu tempo.

oknate
fonte
3
Substituir basicamente um serviço principal tem as mesmas implicações que a implementação de ganchos ALTER. Os riscos ocorrem, mas são bem menores e podem ser mitigados com a documentação de código adequada.
ya.teck
11
É provável que um bom número de substituições, ganchos e patches possa reduzir a manutenção do projeto.
ya.teck
Isso me parece um caso de uso perfeito para um [decorador de serviço] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau
7

Suponho que substituir EntityAutocompleteMatcher afetará todos os elementos de formulário de preenchimento automático em seu site. Para criar um novo plug-in de seleção de entidades, porque é uma abordagem mais granular. O plug-in pode ser ativado por campo. Aqui está um exemplo de um plugin desse tipo. https://drupal.stackexchange.com/a/220136/433

No seu caso, a implementação seria ainda mais trivial:

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

}

O uso de NodeSelection como classe base em vez de DefaultSelection permitiria filtrar os nós referenciados por seus status. Observe que a referência a outros tipos de entidade ainda não é suportada .

Ao contrário do widget de link de referência da entidade, não permite especificar o plug-in de seleção por meio da interface do usuário, portanto, você deve configurá-lo programaticamente usando 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 que o ID do plug-in contenha um ponto-e-vírgula.

ya.teck
fonte
4

Uma outra maneira fácil de modificar o número de resultados é alterar o valor do intervalo na consulta:

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

@Weri, eu evitaria fazer isso, tendo apenas implementado sua sugestão e passado a maior parte do dia tentando corrigir outro problema.

A alteração de consulta que você sugere também afeta a referência inteira ao vincular parágrafos a nós. Um nó em que eu estava trabalhando tinha mais de 80 itens de parágrafo antes de adicionar a alteração. Uma vez adicionado, não consegui salvar o nó. Remover / Comentar a alteração corrigiu o problema.

Atualizar

O agrupamento de $ query-> range () em uma verificação de rota corrige o problema para mim, por exemplo,

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

FWIW, você pode definir a exibição do formulário do campo como "Selecionar lista" em vez de "Preenchimento automático".

Em seguida, você terá todas as opções, embora em um formato menos conveniente, mas sem necessidade de hacks.

ognockocaten
fonte