Recientemente, en uno de nuestros proyectos con Drupal 10, nos enfrentamos a un desafío interesante: implementar "local tasks" de dos niveles para una funcionalidad específica de nuestro módulo. A pesar de la abundante documentación sobre tareas locales en Drupal, configurar dos niveles de estas tareas resultó ser un reto, pues no lográbamos que se mostraran de la forma que necesitábamos. Sin embargo, después de una búsqueda exhaustiva, encontramos un ejemplo en un módulo ya existente que nos ayudó a resolver el problema.
Explorando el Problema
La necesidad era añadir una "local task" principal y tres subtasks asociadas, que se mostraran al visualizar o editar un nodo. Inicialmente, el principal obstáculo fue encontrar la manera correcta de implementar dos niveles de local tasks.
La Solución: Inspiración en Módulos Contribuidos
Fue durante nuestra búsqueda en los módulos contribuidos existentes donde encontramos la inspiración. En Drupal.org existe documentación muy buena, incluso las issues de los módulos muchas veces pueden ofrecer pistas sobre cómo solucionar ciertos problemas o necesidades. Por suerte, la comunidad de Drupal es extensa y existe un alto porcentaje de probabilidades de que alguien ya se haya enfrentado antes con el problema.
La documentación sobre las "local task" de varios niveles ya existe en Drupal.org, puedes verla en "Providing module-defined local tasks", pero teníamos problemas para entender cómo implementarla correctamente.
También buscamos en el módulo Examples un ejemplo que nos pudiera servir.
En este escenario, fue el módulo webform_node
quien nos proporcionó la inspiración. Este módulo gestiona múltiples niveles de tareas locales ya que provee un tipo de contenido webform y define ciertas tasks para ver el número de submissions que se han hecho al webform y permite gestionarlo, y este fue el punto de partida de nuestra solución. Tener el módulo instalado y funcionando nos permitió indagar en sus entrañas y entender qué y cómo necesitábamos implementar.
La Solución: Implementación
No nos vamos a extender demasiado explicando qué es cada cosa, ya que existe documentación muy buena sobre rutas y "local tasks", así que explicaremos únicamente la parte que se "nos atravesó".
Definimos nuestras rutas en el archivo mymodule.routing.yml
de la siguiente manera:
# Definición de la ruta principal.
mymodule.node.main:
# Establece la URL que activa esta ruta, con un placeholder para un nodo específico.
path: '/node/{node}/mymodule/main'
defaults:
# Especifica el título de la página para esta ruta.
_title: 'Main route'
# Indica el formulario que debería usarse al acceder a esta ruta.
_form: 'Drupal\mymodule\Form\MyModuleForm'
# Condiciones que deben cumplirse para acceder a esta ruta.
requirements:
# La ruta requiere que el usuario tenga este permiso específico.
_permission: 'administer site configuration'
# Usa un método de controlador para verificar el acceso personalizado.
_custom_access: '\Drupal\mymodule\Controller\MymoduleNodeController::access'
# Asegura que el placeholder {node} sea un número entero.
node: \d+
options:
# Indica que esta es una ruta de administración, lo cual afecta su visualización en ciertas áreas del backend.
_admin_route: TRUE
parameters:
node:
# Establece que el parámetro {node} debe representar una entidad de tipo nodo.
type: entity:node
También definimos el resto de rutas, asegurando que cada una tuviera los controladores necesarios para manejar sus funciones específicas. Hasta aquí nada nuevo.
La Clave: Dos Niveles en Local Task
El verdadero reto fue configurar las local tasks con jerarquía utilizando el archivo mymodule.links.task.yml
. Aquí descubrimos que la clave para habilitar un segundo nivel radica en el uso adecuado del atributo parent_id
.
En el siguiente ejemplo se define una task principal que apunta a la ruta de "mymodule.node.main
" llamada "mymodule.task_main
", y su ruta base es entity.node.canonical
ya que queríamos que se mostrara junto al resto de tasks del nodo. Se puede ver que la ruta de "mymodule.node.main
" aparece en la definición de dos tasks, ya que es a su vez hija y principal task, y que tanto la ruta de "mymodule.node.main
" como el resto tienen como padre a la task principal "mymodule.task_main
".
# Define la task principal
mymodule.task_main:
# El título de la task que será visible en la interfaz de usuario.
title: 'Main task'
# Especifica el nombre de la ruta a la que está asociada la task.
route_name: mymodule.node.main
# Determina la ruta base donde esta tarea aparecerá en el conjunto de tareas.
base_route: entity.node.canonical
# Controla el orden en el que las tareas aparecen; un valor menor aparece más a la izquierda.
weight: 10
# Define una subtarea bajo la task principal ya definida.
mymodule.subtask_main:
# Título visible para la subtask.
title: 'Main subtask'
# Nombre de la ruta que la subtask utiliza, puede ser el mismo que el de la task principal.
route_name: mymodule.node.main
# Especifica que esta task es un hijo del ID de la tarea principal, definiendo así el segundo nivel de jerarquía.
parent_id: mymodule.task_main
mymodule.subtask_secondary:
title: 'Subtask secondary'
route_name: mymodule.node.secondary
parent_id: mymodule.task_main
mymodule.subtask_other:
title: 'Other subtask'
route_name: mymodule.node.subtask_other
parent_id: mymodule.task_main
Al definir el parent_id
para cada subtask, establecimos la relación jerárquica necesaria para que Drupal pudiera entender y mostrar estas tareas de manera correcta bajo la task principal "mymodule.task_main".
Conclusión
La capacidad de Drupal para ser extensible y altamente personalizable es una de sus grandes fortalezas, y gracias a su comunidad y al código abierto, es posible encontrar ejemplos y soluciones para casi cualquier desafío. A través de la revisión de módulos contribuidos y una comprensión básica de cómo integrar parent_id
en el links.task.yml
, logramos implementar con éxito nuestro sistema de "local tasks" de dos niveles.
Este ejercicio no solo nos permitió solucionar el problema actual, sino que también enriqueció nuestro conocimiento sobre las posibilidades de Drupal y la importancia de una comunidad activa y colaborativa. La próxima vez que te enfrentes a un problema similar, recuerda que a menudo, las soluciones pueden estar a un clic de distancia dentro de un módulo ya existente.