CÓMO agregar enlaces contextuales personalizados a una vista página en Drupal 8

in #drupal8 years ago (edited)

| English | Español |

Apoyarse en enlaces contextuales (contextual links) es una excelente estrategia de usabilidad (UX), porque ayudan a que los site builders Drupal puedan interactuar con las diferentes entidades renderizadas, dentro de una vista página (views page). Por ejemplo, es común encontrar una vista grilla (grid view) en la página de inicio (homepage). Cuando el usuario pasa el mouse encima de la vista grilla, el ícono de enlaces contextuales aparecerá, con un clic es fácil mostrar las opciones disponibles. Por defecto la única opción disponible es editar la configuración de dicha vista grilla:

¿No cree que hay espacio para más enlaces contextuales?

Un caso de uso complejo

Las grillas suelen mostrar un cierto numero de elementos, tal vez cada uno con una foto, un título o una pequeña descripción. Son apenas algunos campos de ciertos tipos de contenido. Los usuarios avanzados apreciarán tener una versión de la vista grilla en "modo edición". Podemos resolver este caso de uso agregando otra vista, con los mismos filtros y ordenamiento, pero con un formato de vista diferente, tal vez una tabla con un formulario de filtros expuestos.

¡Muy útil!

Pero aún ambas vistas no están conectadas y el modo edición no es una opción para todos. Solo ciertos usuarios privilegiados deben tener acceso al modo edición...

¡Enlaces contextuales al rescate!

¿Cómo agregar un enlace contextual para acceder al modo edición desde una vista grilla?

Enlaces contextuales detrás de cámaras

Los enlaces contextuales se construyen sobre las APIs de Drupal y Symfony, con componentes front-end y back-end. El primer requerimiento es tener una ruta, por diseño Drupal solo renderizará enlaces a rutas Symfony válidas, eso hará feliz a los componentes back-end. Por el lado front-end, Drupal necesita que cada enlace contextual tenga un nombre máquina (machine_name) y el controlador es responsable de agregarlos a la página solicitada. ¡Las cosas se han complicado, lo sé! Acostúmbrese a eso porque Drupal es tan poderoso como difícil.

En buen cristiano, los enlaces contextuales son objectos que dependen de otros objectos, los cuales requieren ser declarados antes de ser agregados a la página deseada. Ahora puede leer el párrafo anterior otra vez y la documentación oficial: Providing module-defined contextual links in Drupal 8 (Inglés).

Declarando enlaces contextuales

El módulo mimodulo es el namespace de ahora en adelante. Agregue el archivo YAML mimodulo.links.contextual.yml dentro de la carpeta del módulo con el siguiente contenido:

mimodulo.contextual_links:
  title: 'Edit mode (power users only)'
  route_name: view.view_id.display_id
  group: mimodulo.contextual_links

Note que mimodulo.contextual_links es exactamente el mismo para el ID y el grupo. Además la route_name es un objeto ruta que ya existe. Olvídese de esto y no habrá magia.

Averiguando la route_name para cada vista página

Es requisito que data vista página tengan una ruta exclusiva, por ejemplo: /ruta-a-mivista. Además, el módulo vistas se encarga de crear la correspondiente route_name que terminará en algo como esto: view.mivista.page_1 y view.mivista.page_2. Los nombres de ruta podrían ser simple código arbitrario, pero Drupal tiene una convención para nombramiento:

  • view: es el prefijo para todas las rutas generadas con vistas
  • view_id: es el machine_name de la vista deseada
  • display_id: page_1, page_2, block_1, attachment_2, etc. No es difícil de entender.

Asumamos que nuestra página grilla tiene view.mivista.page_1 como route_name y que view.mivista.page_2 pertenece al modo edición (Nótese page_1 != page_2).

Este caso de uso consiste en enlazar dos vistas, pero usted podría querer agregar enlaces contextuales a otro tipo de ruta, tal vez a un nodo o a un formulario personalizado, así que tiene algo de tarea pendiente.

Reemplazando el controlador de la vista

Cada página servida por Drupal tiene una clase controlador designada, las vistan hacen uso de ViewPageController en nuestro caso de uso particular. Ya que las rutas Drupal son en realidad rutas Symfony, es necesario reemplazar al controlador para poder agregar los enlaces contextuales ya declarados. ¡No se asuste! el controlador es apenas una clase extendida de ViewPageController y hay documentación oficial: altering existing routes and adding new routes based on dynamic ones.

Agregue un archivo YAML mimodulo.services.yml dentro de la carpeta del módulo con el siguiente contenido:

services:
  mimodulo.route_subscriber:
    class: Drupal\mimodulo\Routing\MiModuloRouteSubscriber
    tags:
      - { name: event_subscriber }

Lo único que debe preocuparle aquí es la class, que debe estar almacenada dentro del archivo: src/Routing/MiModuloRouteSubscriber.php con el siguiente contenido:

<?php

namespace Drupal\mimodulo\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class MiModuloRouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  public function alterRoutes(RouteCollection $collection) {
    // Busca el objeto ruta por su route_name
    if ($route = $collection->get('view.mivista.page_2')) {
      // Reemplaza el controlador de la vista con una versión extendida de esta
      $route->setDefault(
        '_controller',
        'Drupal\mimodulo\Routing\ViewPageController::handle'
      );
    }
  }

}

Si usted lee el código anterior cuidadosamente, tiene comentarios explicando cómo el controlador de la vista es reemplazado.

Extendiendo el controlador predeterminado de la vista

Ahora que la vista ya no va a usar ViewPageController por defecto, sino una versión extendida, es tiempo de codear un poco más para completar la tarea. Cree el archivo src/Routing/ViewPageController.php con el contenido siguiente:

<?php

namespace Drupal\mimodulo\Routing;

use Drupal\views\Routing\ViewPageController as BaseController;
use Drupal\Core\Routing\RouteMatchInterface;

class ViewPageController extends BaseController {

  /**
   * {@inheritdoc}
   */
  public function handle($view_id, $display_id, RouteMatchInterface $route_match) {
    // Deja que el controlador original haga su parte
    $build = parent::handle($view_id, $display_id, $route_match);

    //  Asegurarse de agregar el enlace contextual a la vista página deseada
    if ($view_id == 'mivista' && $display_id == 'page_1') {
      $build['#contextual_links']['mimodulo.contextual_links'] = [
        'route_parameters' => [
          'view' => $view_id,
        ],
        'metadata' => [
          'location' => 'page',
          'name' => $view_id,
          'display_id' => 'page_2',
        ],
      ];
    }

    return $build;
  }
}

Conclusión

Se requirió apenas dos archivos YAML, dos clases y cero hooks para completar la tarea. Reconozco que no ha sido tan fácil pero aprendí más acerca de Enrutamiento, Controladores, Symfony, Drupal 8 y Conceptos de POO en el proceso ¡valió la pena!

-- @develCuy

Sort:  

This post has been ranked within the top 50 most undervalued posts in the first half of Mar 12. We estimate that this post is undervalued by $1.66 as compared to a scenario in which every voter had an equal say.

See the full rankings and details in The Daily Tribune: Mar 12 - Part I. You can also read about some of our methodology, data analysis and technical details in our initial post.

If you are the author and would prefer not to receive these comments, simply reply "Stop" to this comment.

Great guide, thanks for the info.

Congratulations @develcuy! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes

Click on any badge to view your own Board of Honnor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

If you want to support the SteemitBoard project, your upvote for this notification is welcome!