¿Cómo mostrar más de 10 elementos en el widget de enlace autocompletar?

10

Esta es una pregunta sobre el módulo Link. Dado que con el módulo Link puede ingresar enlaces externos o internos, confiamos en él.

Desafortunadamente, el número de elementos que se mostrarán desde su campo de autocompletar está limitado a 10. Tenemos muchos nodos con títulos casi idénticos, por lo que resulta que el nodo que estamos buscando no se muestra en el campo de autocompletar cuando hay Más de 10 títulos coincidentes.

El límite está codificado core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php. ¿Existe una manera elegante de aumentar este pequeño número desde un módulo personalizado? ¿Tengo que extender class EntityAutocompleteMatcher? ¿Dónde tendría que colocar mi extensión y cómo asegurarme de que se ejecute desde el widget de enlace?

leymannx
fuente

Respuestas:

10

Si puede vivir anulando todos los límites de autocompletar, puede anular un servicio central en Drupal 8;

El servicio que necesita anular está aquí en core.services.yml:

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

En su módulo personalizado, agregue una clase 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);
  }

}

Luego copie EntityAutocompleteMatcher.php en su módulo en /src/Entity/EntityAutocompleteMatcherCustom.php

Luego actualice el 10 codificado a 50, o el límite que desee:

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, anular los servicios centrales tiene algunos riesgos, pero es genial que puedas hacer esto.

¿Cuáles son los riesgos de anular un servicio central?

1) Puede perder los beneficios de las actualizaciones cuando actualiza Core. Si hay una solución de seguridad crítica en el servicio, y su copia alterada tiene el agujero de seguridad, no se beneficiará de la comunidad que actualiza ese código.

2) Otros módulos que instale pueden tener dependencias del servicio original con su conjunto de características original. Entonces, digamos que hay algún código en otro módulo que se romperá si el número de entradas de autocompletar es mayor o menor que 10, no lo sabrá hasta que lo afecte.

3) Hace que su base de código sea más difícil de mantener. Debe recordar que no está utilizando Drupal principal, sino una versión extendida. Otros desarrolladores que se unen a su proyecto después de su partida pueden tener dificultades para descubrir por qué un servicio se comporta de una manera no estándar.

¿Es este núcleo de piratería?

Depende de cómo lo mires. No está entrando en el módulo central y cambiando el código. Ni siquiera es crear un parche y aplicarlo y rastrearlo con un administrador de paquetes como el compositor. Es más una personalización única que altera el comportamiento central de un sitio, similar a un gancho ALTER. Es más autónomo que un hack core, porque está dentro de su propio módulo personalizado en su sitio. Por lo tanto, las actualizaciones principales del servicio original no se verán afectadas, de la misma manera que si parcheara o pirateara el código de servicio original.

Pero tiene algunos de los mismos riesgos que hackear el núcleo, como se mencionó anteriormente.

En la pregunta original, el problema era que los títulos de los nodos no son lo suficientemente únicos. La mejor solución, además de cambiar el límite global de los menús desplegables, sería resolver el problema de la unicidad.

Lo que sugeriría es agregar un nuevo campo field_display_title y usarlo en la página, y si lo necesita, otro field field_teaser_title para mostrar en las páginas de la lista donde necesita un título más corto. Luego, el título real que aparece en el menú desplegable de selección de referencia de entidad puede ser útil para sus editores y puede ser único, como "Mi artículo (página 1)" si el problema es que cada página tiene el mismo título. Entonces no tiene que anular un servicio central.

Cuando encuentre un problema con Drupal, intente encontrar la solución que requiera la menor cantidad de código personalizado. Esto hace que su sitio sea más estable, más fácil de mantener y le ahorra tiempo.

oknate
fuente
3
Básicamente, anular un servicio central tiene las mismas implicaciones que implementar los ganchos ALTER. Los riesgos tienen lugar, pero son bastante menores y pueden mitigarse con la documentación de código adecuada.
ya.teck
1
De hecho, un buen número de tales anulaciones, ganchos y parches pueden reducir la capacidad de mantenimiento del proyecto.
ya.teck
Esto me parece un caso de uso perfecto para un [decorador de servicios] ( blueoakinteractive.com/blog/service-decorators-drupal-8 ).
Beau
7

Supongo que anular EntityAutocompleteMatcher afectará a todos los elementos de formulario de autocompletar en su sitio. Para que cree un nuevo complemento de selección de entidad en su lugar porque es un enfoque más granular. El complemento podría habilitarse por campo. Aquí hay un ejemplo de dicho complemento. https://drupal.stackexchange.com/a/220136/433

En su caso, la implementación sería aún más trivial:

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

}

Usar NodeSelection como clase base en lugar de DefaultSelection le permitiría filtrar los nodos referenciados por su estado. Tenga en cuenta que todavía no se admite la referencia a otros tipos de entidades .

A diferencia del widget de enlace de referencia de entidad, no permite especificar el complemento de selección a través de la interfaz de usuario, por lo tanto, debe configurarlo mediante programación 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';
}

Es importante que la ID del complemento contenga un punto y coma.

ya.teck
fuente
4

Otra forma fácil de modificar el número de resultados es alterar el valor del rango en la 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
fuente
1

@Weri, evitaría hacerlo, ya que implementé su sugerencia y pasé la mayor parte del día tratando de solucionar otro problema.

La consulta que usted sugiere también afecta a toda referencia de referencia al vincular párrafos a nodos. Un nodo en el que estaba trabajando tenía más de 80 párrafos antes de agregar el alter. Una vez agregado, no pude guardar el nodo. Eliminar / comentar el alter solucionó el problema.

Actualizar

Ajustar el $ query-> range () en una verificación de ruta soluciona el problema para mí, por ejemplo,

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

FWIW, puede configurar la visualización del formulario del campo en "Seleccionar lista" en lugar de "Autocompletar".

Luego, obtendrá todas las opciones, aunque en un formato menos conveniente, pero no se requieren hacks.

ognockocaten
fuente