0% encontró este documento útil (0 votos)
32 vistas453 páginas

Forcontu d8 Back End Development3 Completo

Cargado por

Ludwring Liccien
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
32 vistas453 páginas

Forcontu d8 Back End Development3 Completo

Cargado por

Ludwring Liccien
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

USO EXCLUSIVO PARA: DATOS DE DESCARGA:

Nombre: LUDWRING ANTHONY Fuente: [Link]


LICCIEN AZOCAR Fecha: 31/07/18 23:08
Cédula de identidad (CI): IP: [Link]
12762471 Código de verificación:
Email: [Link]@[Link] EBOD8BD300055495000774

Versión 1.1 -3/10/2017

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
Experto en Drupal 8
Back-End Development III

Fran Gil

Versión: 1.1 (3/10/2017)


Experto en Drupal 8 Back-End Development III

Colección Aprende Drupal con Forcontu

Copyright © 2011-2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este libro, tanto en su totalidad como parcialmente no
puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier medio ya sea
electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización expresa y escrita
por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o puesta a disposición
como resúmenes, reseñas o revistas de prensa, fines para los que también será necesario contar con la
correspondiente autorización de Forcontu S.L. Para obtener más información, póngase en contacto a
través de info@[Link].

ISBN-13 (Edición impresa): 978-84-945012-4-1


ISBN-13 (Edición electrónica, PDF): 978-84-945012-5-8

Autor: Fran Gil

Diseño de portada: Ateigh Design


Directora de Marketing y Distribución: Laura Mª Fornié Alonso

Editado por Forcontu S.L.


Impreso por Liberis Site S.L.

Aunque esta publicación ha sido desarrollada con gran cuidado, ni Forcontu S.L., ni los autores serán
responsables de los posibles errores y las consecuencias que pueda causar, directa o indirectamente, la
información contenida en este trabajo. Por favor, dirija cualquier recomendación o comentario a
info@[Link].

Ayúdenos a mejorar los libros reportando cualquier error que encuentre. Puede hacerlo directamente
completando este formulario: [Link]

Para más información sobre el contenido de este libro o sobre los canales de distribución, escriba
directamente a info@[Link] o visite la página web [Link].

USO EXCLUSIVO PARA:


Nombre: LUDWRING ANTHONY LICCIEN AZOCAR
Cédula de identidad (CI):
12762471
Email: [Link]@[Link]

DATOS DE DESCARGA:
Fuente: [Link]
Fecha: 31/07/18 23:08
IP: [Link]
Código de verificación: EBOD8BD300055495000774
EBOD8BD300055495000774

Presentación

Presentación
Estoy seguro de que si has llegado hasta aquí es porque deseas
conocer Drupal a fondo. No importa si sólo has oído hablar de
sus numerosas ventajas con respecto a otros CMS, o si ya lo
conoces y quieres seguir profundizando en su estudio.

El gran reto al que te enfrentas es, sin duda, superar la


pronunciada curva de aprendizaje de Drupal. No, no se
trata de un mito, llegar a dominar Drupal es una meta realmente
costosa en términos de esfuerzo y dedicación.

Esta meta se complica si lo intentas por tu cuenta, sin una guía


didáctica más allá de las necesidades específicas de tu primer
proyecto. Te verás navegando entre miles de recursos
disponibles en Internet (vídeos, entradas de blog,
documentación de módulos, etc.) y consultando en foros o chats buscando solucionar con urgencia
problemas particulares. Aún así avanzarás en tu proyecto y en tu conocimiento de Drupal, pero los
vacíos de conocimiento que se van generando harán que la duda te perseguirá permanentemente con
cuestiones como: ¿lo estoy haciendo correctamente?, ¿existirá un módulo mejor para
implementar esta funcionalidad?, ¿si utilizo este módulo tendré problemas más adelante
para integrar otros módulos?, etc. Y eso sin hablar del tiempo que te llevará convertirte en un
experto en Drupal por ese camino, que puede superar perfectamente los 3 años suponiendo una
dedicación exclusiva al desarrollo de sitios web con Drupal.

No te voy a engañar, con nuestros planes formativos no te ahorrarás ni el esfuerzo ni la


dedicación, pero te acompañaremos y guiaremos en el aprendizaje de Drupal para que lo
domines en el menor tiempo posible y puedas desarrollar tus proyectos de forma óptima,
productiva y con garantías de éxito.

Sabrás que te has convertido en un experto en Drupal cuando puedas desarrollar un proyecto
sintiéndote seguro y cómodo, y eso se consigue con la base necesaria para no perder tiempo en lo
cotidiano y focalizar así tus esfuerzos en afrontar los nuevos retos que vayan surgiendo. Porque sí,
Drupal es un entorno en continua evolución y siempre tendrás nuevos retos a los que enfrentarte,
nuevas cosas que aprender y nuevos módulos que probar. Siempre tendrás dudas que consultar, pero
notarás que el nivel de tus consultas va subiendo junto con tu grado de experiencia.

Desde 2009 han sido muchas las personas que han aprendido Drupal con los libros y cursos de Forcontu,
primero de Drupal 6 y luego de Drupal 7, y que después de todo este tiempo han seguido desarrollando
sus proyectos con Drupal y comparten con nosotros su progreso y, como no, su ilusión por dar el salto
a Drupal 8 con nosotros.

Ahora comienza, para todos, una nueva etapa con un Drupal 8 más profesional y más complejo.
Drupal 8 ha dado un importante salto evolutivo con respecto a Drupal 7, por lo que la inversión en
adaptación y reciclaje se hace necesaria incluso si llevas años trabajando con Drupal.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development II v


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Presentación

En Forcontu estamos desarrollando unos planes formativos para Drupal 8 mucho más completos
que los anteriores, que te permitan aprender tanto Drupal como otras tecnologías que son necesarias
para la implementación de sitios web modernos. De hecho, para esta nueva versión hemos separado
los libros y cursos en tres ramas o perfiles profesionales: Site Building, Back-End Development y
Front-End Development. De esta forma podrás especializarte exclusivamente en uno de los perfiles
y obtener tu certificado de Experto en Drupal 8 (Site Building, Back-End Development o Front-
End Development), o atreverte con las tres disciplinas y obtener el certificado de Máster en Drupal
8, que te certifica como desarrollador Full-Stack, que es el perfil laboral relacionado con Drupal más
demandado.

Desde 2012 nuestros libros y cursos están disponibles en español e inglés, y los certificados también se
emiten en ambos idiomas para que puedas ampliar tus opciones laborales.

Si ya te has comprometido con Drupal, permítenos acompañarte en esta nueva etapa de tu carrera
profesional y guiarte para lograr tu objetivo de convertirte en un Experto en Drupal 8.

Fran Gil
Co-fundador y CEO Forcontu
[Link]@[Link]
@frankgil76

vi Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

Curso Online Experto en Drupal 8


0
Back-End Development

0.1 Descripción del curso

Qué es Back-End Development

El desarrollo Back-End en Drupal 8 hace referencia a la programación de módulos y componentes


que permiten ampliar la funcionalidad del sistema. Se trata principalmente de programación en PHP
orientada a objetos, aunque también veremos otras tecnologías necesarias durante el desarrollo de un
proyecto web.

Modalidad de impartición

El curso de Experto en Drupal 8 Back-End Development es un curso 100% práctico y


tutorizado, en modalidad Online (Teleformación).

Los participantes dispondrán, además del aula virtual, de un alojamiento web para realizar las prácticas
propuestas en el curso. El alumno podrá estudiar y practicar en cualquier momento del día, adaptando
el temario y las actividades a sus necesidades profesionales o particulares.

Duración del curso

El curso de Experto en Drupal 8 Back-End Development tiene una duración estimada de 5 meses
y 300 horas certificadas, con una dedicación semanal de 15 horas. En este período se incluye la
realización de las actividades (240 horas) y del Proyecto Final (60 horas).

Para que puedas tener más margen y tomarte algunas semanas de descanso, tendrás 6 meses para
completarlo. Ten en cuenta que éste es el tiempo máximo, pero si le dedicas más horas o tienes
experiencia previa en Drupal y/o en programación, seguro que puedes completarlo en menos tiempo.

Además, si al finalizar el período de matrícula no has podido completar el curso, siempre podrás ampliar
la matrícula mes a mes, sin perder todo el trabajo realizado y sin que afecte a tu calificación final.

Ampliación de matrícula

Los alumnos que no hayan podido completar el curso en el tiempo establecido, podrán ampliar la
matrícula por períodos mensuales, hasta finalizar el curso. Consulta en nuestra web el coste adicional
de esta ampliación.

Objetivos del curso

El objetivo principal del curso Experto en Drupal 8 Back-End Development es la capacitación del
alumno como desarrollador Back-End para el desarrollo de módulos personalizados con Drupal 8.

Para llegar a este objetivo es necesario cumplir estos objetivos secundarios:

- Conocer las principales herramientas y lenguajes de programación. Drupal 8 requiere


un conocimiento más amplio de PHP y de programación orientada a objetos. Además,
trabajaremos con herramientas de programación que facilitan el trabajo del desarrollador y lo
hacen más productivo.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III vii
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

- Conocer la API y estructuras de programación de Drupal 8. Es necesario conocer la API


de Drupal 8 y las nuevas estructuras disponibles.

- Crear módulos desde cero y extender módulos existentes. Además de crear módulos
desde cero, también aprenderemos a extender y modificar funcionalidades aportadas por el
núcleo o por cualquier módulo contribuido.

- Adaptación a cambios en la API. El alumno tendrá los conocimientos necesarios para


adaptarse a los continuos cambios de versión de Drupal 8 y realizar los cambios que se puedan
requerir en el código de sus módulos.

- Aprender a contribuir con la comunidad. El éxito de Drupal no habría sido posible si no


contara con miles de entusiastas, usuarios y desarrolladores, que han compartido sus
desarrollos, conocimientos y experiencias con la comunidad. Por ello consideramos que,
siempre que sea posible, debemos contribuir con la comunidad, ya sea compartiendo y
manteniendo los módulos desarrollados, informando de errores y soluciones, y ayudando a
otros usuarios.

Para qué te prepara

A través de este curso aprenderás a programar módulos personalizados de cualquier tipo para Drupal
8. También aprenderás a utilizar las tecnologías y herramientas necesarias para abordar esta tarea de
forma productiva.

La capacitación adquirida te será de gran ayuda para introducirte en el mercado laboral, mejorar en tu
puesto de trabajo o incluso emprender un proyecto propio basado en tecnologías web.

A quién va dirigido

El curso de Experto en Drupal 8 Back-End Development va dirigido a:

- Desarrolladores con conocimientos de Drupal 8 a nivel de Site Building que desean profundizar
en la programación de módulos personalizados. Se recomienda cursar primero el curso de
Experto en Drupal 8 Site Building.

- Cualquier persona que, sin conocimientos de programación previos, desea reorientar su carrera
profesional hacia la programación y el desarrollo de sitios web con Drupal 8.

Conocimientos previos necesarios

Para acceder al curso de Experto en Drupal 8 Back-End Development no son necesarios conocimientos
de programación. El curso incluye una buena base de programación para personas con ningún o pocos
conocimientos de programación. Aún así, debes entender que, si no tienes conocimientos previos de
programación, se trata de un reto muy importante que requerirá un esfuerzo adicional al de otros
compañeros que sí cuenten con una base de programación.
Aunque no se requiere una titulación académica específica, por su orientación profesionalizadora, es
recomendado para diseñadores, desarrolladores web y programadores con titulaciones técnicas
universitarias o de formación profesional.

Requisitos técnicos

Para realizar el curso es necesario disponer de un ordenador con conexión a internet y navegador web.
Durante el curso te iremos indicando las aplicaciones que debes instalar (cliente FTP, entorno de
desarrollo, etc.).

viii Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

Qué incluye el curso

El curso de Experto en Drupal 8 Back-End Development incluye los siguientes servicios y materiales:

- Libros. Existe mucha información sobre Drupal disponible en libros, blogs, foros, etc. En
Forcontu hemos desarrollado unos materiales formativos organizados, en español y
compuestos de diferentes objetos de aprendizaje para asegurar la capacitación profesional en
Drupal. El curso incluye los libros:

o Experto en Drupal 8 Back-End Development I


o Experto en Drupal 8 Back-End Development II
o Experto en Drupal 8 Back-End Development III

La versión en PDF de los libros se irá actualizando periódicamente para ir incorporando los
cambios que se vayan produndiendo en los contenidos abordados.

- Aula virtual. Dentro del aula encontrarás más información sobre los pasos a seguir durante
el curso y las actividades a realizar.

- Alojamiento web para la realización de las actividades propuestas y del Proyecto Final de
experto.

- Evaluación continua de las actividades realizadas durante el curso, aportando feedback


sobre los posibles errores cometidos. El aula dispone de un sistema de autocorrección que
comprobará inmediatamnete si has realizado la actividad correctamente. Esto hace que no
tengas que depender del tutor para la corrección de las actividades.

- Pruebas de Evaluación Continua. Son conjuntos de actividades adicionales para evaluar


un bloque de unidades del curso

- Evaluación del proyecto final de Experto.

- Seguimiento del progreso del alumno. El tutor hará un seguimiento continuo de tu


progreso y responderá a las dudas que te surjan en los foros del aula.

- Certificado de Experto Forcontu en Drupal 8 Back-End Development tras superar el


curso.

- Posibilidad de obtener el Certificado de Máster Forcontu en Drupal 8 al completar el resto


de cursos de Experto.

- Carta de recomendación, para los alumnos que hayan superado el curso con una calificación
superior al 95%.

- Incorporación a la bolsa de trabajo, donde recibirás las ofertas que publiquen las
empresas interesadas en desarrolladores con conocimientos de Drupal.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III ix


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

0.2 Contenidos

El curso de Experto en Drupal 8 Back-End Development se divide en las siguientes unidades,


distribuidas en tres libros:

Libro Experto en Drupal 8 Back-End Development I:

- Unidad 1. Preparando el entorno para programar en Drupal y PHP


- Unidad 2. Servidores web AMP (Apache + MySQL + PHP)
- Unidad 3. Introducción a la consola de Linux
- Unidad 4. Introducción al control de versiones con Git
- Unidad 5. PHP I: Introducción a PHP
- Unidad 6. PHP II: Funciones de la API de PHP
- Unidad 7. PHP III: Programación orientada a objetos (POO)
- Unidad 8. PHP IV: Patrones de diseño
- Unidad 9. MySQL I: Introducción a SQL y MySQL
- Unidad 10. MySQL II: Herramientas de gestión de la base de datos
- Unidad 11. Symfony I: Introducción a Symfony
- Unidad 12. Symfony II: Componentes de Symfony

Libro Experto en Drupal 8 Back-End Development II:

- Unidad 13. Drush I: Comandos de Drush


- Unidad 14. Drupal Console I: Comandos de Drupal Console
- Unidad 15. Arquitectura de Drupal 8
- Unidad 16. Buenas prácticas de desarrollo con Drupal
- Unidad 17. Introducción a la creación de módulos
- Unidad 18. Enrutamiento y menús
- Unidad 19. Introducción a la API de Configuración
- Unidad 20. Bases de datos I: Creación de tablas
- Unidad 21. Bases de datos II: Sentencias select, insert, update y delete
- Unidad 22. Formularios I: Creación de formularios
- Unidad 23. Formularios II: Elementos de formulario
- Unidad 24. Formularios III: Ampliación de formularios

Libro Experto en Drupal 8 Back-End Development III:

- Unidad 25. Plugins I: Introducción a plugins y bloques


- Unidad 26. Usuarios y permisos
- Unidad 27. Theming I: Creación de temas y plantillas
- Unidad 28. Theming II: Theming en módulos
- Unidad 29. Plugins II: Tipos de plugins y servicios
- Unidad 30. Entidades I: Entidades de configuración
- Unidad 31. Entidades II: Entidades de contenido
- Unidad 32. Formatos de texto y filtros
- Unidad 33. Archivos e imágenes
- Unidad 34. Sistema de búsqueda
- Unidad 35. Traducción de módulos
- Unidad 36. Formularios IV: jQuery y Ajax
- Unidad 37. Programación de actualizaciones
- Unidad 38. Tests automáticos
- Unidad 39. Otras funcionalidades
- Unidad 40. Compartir en comunidad

x Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

0.3 Metodología

Metodología

El curso de Experto en Drupal 8 Back-End Development es un curso 100% práctico y


tutorizado, en modalidad Online-Teleformación.

Dispondrás de una guía de trabajo con los pasos a seguir: lectura de cada unidad y prácticas a realizar.
A través de los foros del aula virtual, el tutor te guiará en tu proceso de aprendizaje, resolviendo las
dudas que te vayan surgiendo durante el curso. También podrás consultar las dudas planteadas por el
resto de participantes e incluso intervenir ayudando a tus compañeros.

Se te facilitará un alojamiento web individual para realizar las actividades. Nuestros alojamientos están
preparados especialmente para alojar sitios web en Drupal 8. Al trabajar en un servidor remoto, el tutor
tendrá acceso a tu sitio web y podrá ver tu trabajo y guiarte en el desarrollo de las actividades
propuestas.

Cuando completas y envias una actividad, el sistema intentará corregirla automáticamente. Si detecta
algún error, te guiará para corregirla. De esta forma podrás tener un feedback de tu trabajo en cualquier
momento, sin depender de la intervención del tutor. Aún así, el tutor estará disponible para resolver
cualquier duda y para corregir manualmente aquellas actividades que el autocorrector no haya sido
capaz de evaluar.

Podrás realizar las prácticas en cualquier momento, adaptando el temario y las actividades a tu ritmo
de trabajo, pero siempre teniendo en cuenta la fecha final de finalización del curso.

El curso se completa con un Proyecto Final acorde a cada especialidad. En el curso de Back-End
Development, el Proyecto Final consiste en implementar un módulo, ampliar un módulo existente o
solucionar errores de módulos constribuidos en [Link]. Si ya tienes un proyecto en mente, podrás
aprovechar el Proyecto Final para realizarlo y recibir la ayuda y feedback del tutor.

Plataforma educativa

El estudiante, una vez matriculado en el curso, será dado de alta en la plataforma educativa y el aula
correspondiente al módulo a cursar.

Los tutores y estudiantes podrán comunicarse libremente a través de los foros (de forma pública para
todo el grupo), o del correo interno (de forma privada, sólo para los destinatarios indicados)
habilitados para cada curso. Por defecto se enviará un duplicado de estas comunicaciones al correo
electrónico externo facilitado por el estudiante.

Los estudiantes de un mismo grupo podrán contactar entre sí y compartir sus experiencias y resultados
con las actividades y materiales propuestos.

El aula se distribuye de la siguiente forma:

- Foro Guía (foro del tutor). En este foro sólo pueden publicar los tutores, y servirá para
comunicar a todo el grupo cuestiones generales sobre el curso u otras cuestiones que
consideren de interés.
- Foro Aprende (foro del curso). En este foro podrán publicar tanto tutores como estudiantes,
y se utilizará para discutir cuestiones relacionadas con Drupal y el curso. Los estudiantes
podrán plantear aquí sus dudas sobre las actividades y otras consultas generales sobre Drupal.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III xi


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

- Correo interno. Permite enviar mensajes a otros compañeros del aula, tanto tutores como
estudiantes.
- Participantes. Listado de participantes del curso, incluyendo tutores y estudiantes.
- Usuarios en línea. Muestra un listado con los participantes que están conectados en ese
momento.
- Perfil. Permite administrar el perfil del usuario: modificar la contraseña de acceso, modificar
los datos personales, cambiar la foto, etc.
- Calificaciones. Permite consultar las calificaciones obtenidas.

0.4 Evaluación y Certificación

Evaluación continua

La puntuación máxima que se puede obtener en el curso es de 1000 puntos, y es necesario completar
al menos 800 para superarlo y obtener el certificado.

Los puntos se distribuyen de la siguiente manera:

- Actividades. Cada unidad de estudio tendrá 1 o más actividades. Las actividades pueden
tener una puntuación variable en función del esfuerzo estimado requerido. Máximo: 700 puntos

Evaluación: Las actividades no se califican con una puntuación intermedia. Si la actividad


enviada no es correcta, se devolverá al alumno con las indicaciones necesarias para solucionar
los errores y volver a enviarla. El alumno puede enviar una actividad tantas veces como desee,
sin penalización en la calificación final. Una vez la actividad sea validada, el alumno obtendrá
la puntuación máxima.

- Pruebas de Evaluación Continua (PEC). Cada PEC consta de un conjunto de actividades


que sirven de repaso y refuerzo sobre lo estudiado en unidades anteriores. Cada actividad de
la PEC se evalúa de forma independiente. El total de puntos de cada PEC es de 50 puntos.
Durante el curso se realizarán 2 PEC. Máximo: 100 puntos

Evaluación: La puntuación final de cada PEC depende de la puntuación obtenida en las


actividades que la componen. Las actividades no se califican con una puntuación intermedia.
Al igual que con las actividades generales, el alumno puede enviar una actividad tantas veces
como desee, sin penalización en la calificación final.

- Proyecto Final. El trabajo final es un proyecto libre que será previamente validado por el
tutor. Máximo: 200 puntos.

El Proyecto Final consta de dos partes:


o Fase 1. Planteamiento y análisis. Máximo: 50 puntos.
o Fase 2. Desarrollo. Máximo: 150 puntos

Evaluación: Al término de cada fase, el tutor evaluará el trabajo realizado y emitirá una
calificación. Si el alumno no obtiene la calificación máxima, recibirá el feedback necesario para

xii Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Curso Online Experto en Drupal 8 Back-End Development

realizar las correcciones o ampliaciones necesarias. El alumno puede volver a enviar el proyecto
para una nueva evaluación.

Forcontu cuenta con un Sistema de Autocorrección de actividades que evalúa de forma


automáticamente cada actividad, aportando en el momento del envío la calificación y el feedback sobre
los aciertos y errores cometidos en caso de que los haya. Dado que la autocorrección se realiza a nivel
de programación, es posible que una pequeña desviación en la solución provoque que el sistema dé por
no válida una actividad aparentemente correcta. Si el alumno considera que la autocorrección no está
validando correctamente la actividad, podrá enviarla al tutor para su corrección manual.

Certificación

Para superar el curso será necesario completar al menos 800 puntos de los 1000 puntos disponibles
(80%). Una vez alcanzados los 800 puntos, el alumno puede dar por completado el curso, o continuar
hasta alcanzar la calificación máxima.

Una vez completado el curso, y siempre que se haya alcanzado los 800 puntos, se emitirá el certificado
de Experto Forcontu en Drupal 8 Back-End Development.

El certificado incluye, entre otros datos, el número de horas de trabajo, el contenido detallado del curso
y la calificación final obtenida.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III xiii
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Índice de contenidos

Presentación ........................................................................................................... v
Curso Online Experto en Drupal 8 Back-End Development .................................. vii
Descripción del curso .............................................................................................................. vii
Contenidos ............................................................................................................................. x
Metodología ............................................................................................................................ xi
Evaluación y Certificación ........................................................................................................ xii
Índice de contenidos ............................................................................................ xv
Unidad 25. Plugins I: Introducción a plugins y bloques ........................................ 3
25.1 Introducción a plugins en Drupal 8 .................................................................................... 4
25.2 Implementación de bloques .............................................................................................. 5
25.3 Configuración por defecto de bloques ................................................................................ 8
25.4 Configuración personalizada de bloques ............................................................................. 9
25.5 Inyección de servicios en plugins ..................................................................................... 15
25.6 Control de acceso a bloques ............................................................................................ 18
25.7 Integrar un formulario en un bloque ................................................................................ 19
25.8 Modificación de bloques de otros módulos ........................................................................ 25
25.9 Hazlo desde la consola ................................................................................................... 27
Unidad 26. Usuarios y permisos ........................................................................... 33
26.1 Obteniendo el usuario actual ........................................................................................... 34
26.2 Roles, permisos y control de acceso ................................................................................. 36
26.3 La entidad User ............................................................................................................. 43
26.4 Operaciones CRUD y hooks de Entity API ......................................................................... 46
26.5 Hooks del módulo User ................................................................................................... 49
26.6 Información adicional de usuario ..................................................................................... 50
26.7 Otras clases y servicios relacionados con usuarios ............................................................. 52
26.8 Hazlo desde la consola ................................................................................................... 53
Unidad 27. Theming I: Creación de temas y plantillas ......................................... 57
27.1 Creación de temas ......................................................................................................... 58
27.2 Archivos de plantilla ....................................................................................................... 72
27.3 Twig en Drupal .............................................................................................................. 87
27.4 Funciones de preprocesamiento de plantillas ................................................................... 102
27.5 Formulario de configuración del tema ............................................................................. 106
27.6 Hazlo desde la consola .................................................................................................. 109
Unidad 28. Theming II: Theming en módulos .................................................... 115
28.1 Arrays renderizables ...................................................................................................... 116
28.2 Elementos renderizables ................................................................................................ 123
28.3 Definición de plantillas en módulos ................................................................................. 127
28.4 Crear extensiones de Twig ............................................................................................. 134
28.5 Hazlo desde la consola .................................................................................................. 136
Unidad 29. Plugins II: Tipos de plugins y servicios ........................................... 141
29.1 Crear un tipo de plugin .................................................................................................. 142
29.2 Crear un servicio personalizado ...................................................................................... 149
29.3 Hazlo desde la consola .................................................................................................. 153
Unidad 30. Entidades I: Entidades de configuración ......................................... 161
30.1 Introducción a Entity API y tipos de entidades ................................................................. 162
30.2 Operaciones sobre entidades ......................................................................................... 164
30.3 Entidades de configuración (Configuration entities) .......................................................... 171
30.4 Hazlo desde la consola .................................................................................................. 182

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III xv


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Índice de contenidos

Unidad 31. Entidades II: Entidades de contenido .............................................. 187


31.1 Entidades de contenido (Content entities) ....................................................................... 188
31.2 Crear un tipo de contenido personalizado ........................................................................ 204
31.3 Crear tipos de campos personalizados ............................................................................. 211
31.4 Theming de entidades ................................................................................................... 219
31.5 Programación de taxonomías ......................................................................................... 221
31.6 Validación de entidades y campos .................................................................................. 224
31.7 Acceso a rutas y operaciones sobre entidades ................................................................. 231
31.8 Hazlo desde la consola .................................................................................................. 232
Unidad 32. Formatos de texto y filtros ............................................................... 239
32.1 Administración de formatos de texto y filtros ................................................................... 240
32.2 Crear un filtro personalizado .......................................................................................... 243
32.3 Crear un formato de texto ............................................................................................. 247
Unidad 33. Archivos e imágenes ........................................................................ 253
33.1 El sistema de archivos de Drupal .................................................................................... 254
33.2 Funciones de archivos ................................................................................................... 259
33.3 La entidad File .............................................................................................................. 262
33.4 Hooks relacionados con archivos .................................................................................... 264
33.5 Formularios con archivos ............................................................................................... 266
33.6 Control de permisos sobre archivos................................................................................. 269
33.7 Presentación de imágenes con estilos ............................................................................. 270
33.8 Efecto de imagen personalizado ..................................................................................... 272
33.9 Hazlo desde la consola .................................................................................................. 273
Unidad 34. Sistema de búsqueda ....................................................................... 277
34.1 Introducción al sistema de búsqueda .............................................................................. 278
34.2 Búsqueda de nodos ....................................................................................................... 281
34.3 Página de búsqueda personalizada ................................................................................. 285
34.4 Hazlo desde la consola .................................................................................................. 290
Unidad 35. Traducción de módulos .................................................................... 295
35.1 Traducción de la interfaz ............................................................................................... 296
35.2 Archivos de traducción .................................................................................................. 300
35.3 Traducción de cadenas fuera del código PHP ................................................................... 305
Unidad 36. Formularios IV: jQuery y Ajax .......................................................... 311
36.1 Introducción a JavaScript en Drupal ................................................................................ 312
36.2 Introducción a jQuery .................................................................................................... 320
36.3 Librerías jQuery en el núcleo .......................................................................................... 329
36.4 Carga condicional de elementos de formulario (#states) ................................................... 333
36.5 Ajax en Drupal .............................................................................................................. 337
36.6 Ajax en formularios ....................................................................................................... 344
36.7 Autocompletado de elementos ....................................................................................... 348
Unidad 37. Programación de actualizaciones .................................................... 353
37.1 Funciones de actualización ............................................................................................. 354
37.2 Actualización de entidades y campos .............................................................................. 356
37.3 Actualización de configuración ........................................................................................ 359
37.4 Actualización de la base de datos ................................................................................... 361
37.5 Hazlo desde la consola .................................................................................................. 363
Unidad 38. Tests automáticos ............................................................................ 369
38.1 Qué son los tests automáticos ........................................................................................ 370
38.2 Ejecución de tests ......................................................................................................... 371
38.3 Tests unitarios .............................................................................................................. 373
38.4 Tests funcionales con SimpleTest ................................................................................... 383
38.5 Tests funcionales con PHPUnit ....................................................................................... 388

xvi Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Índice de contenidos

Unidad 39. Otras funcionalidades ...................................................................... 395


39.1 Envío de emails............................................................................................................. 396
39.2 Programación de tareas de cron ..................................................................................... 403
39.3 Logging API .................................................................................................................. 405
Unidad 40. Compartir en comunidad .................................................................. 411
40.1 La Comunidad de Drupal ............................................................................................... 412
40.2 Reportar incidencias ...................................................................................................... 415
40.3 Compartir un proyecto en [Link] .............................................................................. 419
40.4 Crear y aplicar parches .................................................................................................. 428
40.5 Colaborar en la traducción de Drupal .............................................................................. 432
Diario de cambios (Change log) ......................................................................... 435

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III xvii
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Plugins I: Introducción a plugins y


25
bloques
En esta unidad realizamos una primera introducción a los plugins en
Comparativa D8/D7
Drupal 8. Los plugins se pueden considerar piezas o componentes
adicionales que podemos integrar en el sistema para añadir una Programación de bloques
funcionalidad específica. Los plugins que desarrollan una misma En sistema de plugins es completamente
funcionalidad, se generan o instancian a partir de un mismo tipo de nuevo en Drupal 8 y no guarda relación
con Drupal 7. Por ejemplo, los bloques se
plugin.
implementan ahora como plugins de tipo
Block, en lugar de a través de hooks,
Algunos de los tipos de plugin disponibles en el núcleo de Drupal son como hacíamos en Drupal 7.
los bloques, los efectos de imagen, los tipos de campo o los controles
y formateadores de campos.

Comenzamos este primer acercamiento a los plugins viendo cómo


implementar plugins de tipo bloque. En próximas unidades
profundizaremos en la estructura interna de los plugnis, viendo cómo
desarrollar nuevos tipos de plugins.

Contenidos de la Unidad
25.1 Introducción a plugins en Drupal 8

25
25.2 Implementación de bloques
25.3 Configuración por defecto de bloques
25.4 Configuración personalizada de bloques
25.5 Inyección de servicios en plugins
25.6 Control de acceso a bloques
25.7 Integrar un formulario en un bloque
25.8 Modificación de bloques de otros módulos
25.9 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 3


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

25.1 Introducción a plugins en Drupal 8

Los plugins se pueden considerar piezas o componentes adicionales que podemos


integrar en el sistema para añadir una funcionalidad específica. Los plugins que
desarrollan una misma funcionalidad, se generan o instancian a partir de un mismo
tipo de plugin.

Algunos de los tipos de plugin disponibles en el núcleo de Drupal son los bloques,
los efectos de imagen, los tipos de campo o los controles y formateadores de
campos.

Cada tipo de plugin tiene su propio servicio "plugin manager", que es una clase
que implementa la interfaz PluginManagerInterface y que se encarga de definir qué
método se utilizará para descubrir o localizar los plugins de ese tipo que se hayan
creado en el sistema (annotation, hook, YAML, etc.), y cómo se crearán o
instanciarán plugins de ese tipo.

Generalmente los tipos de plugin incluyen una clase base para facilitar la
instanciación de plugins. Por ejemplo, para crear un plugin de Bloque, crearemos
una clase que extienda a la clase BlockBase.

Los plugins basados en Annotations utilizan esta nomenclatura para ser


registrados en el sistema y descubiertos por el plugin manager. Cada tipo de plugin
utilizará sus propias directivas (@Block para bloques, @FieldFormatter para
formateadores de campos, etc.).

Como ejemplo de creación de plugins, en esta unidad veremos cómo implementar


plugins de tipo bloque. En próximas unidades veremos cómo desarrollar nuevos
tipos de plugins y cómo instanciar plugins de otros tipos de plugins del sistema.

Plugin API overview


[Link]

4 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Implementación de bloques 25.2

Los bloques se crean implementando un plugin de tipo Block. Para la definición de


un bloque tenemos que tener en cuenta:

1. El bloque es un plugin que se define creando una nueva clase que


implementa la interfaz \Drupal\Core\Block\BlockPluginInterface. En la
práctica, extenderemos la clase \Drupal\Core\Block\BlockBase, que a su
vez implementa la interfaz.

2. La clase del bloque se creará dentro de la carpeta /src/Plugin/Block, en


un archivo independiente con el mismo nombre que la clase.

3. En los bloques utilizamos Annotations, a través de la directiva @Block,


para registrarlos en el sistema y añadir información de configuración
adicional.

Nos quedamos inicialmente con estos dos puntos, que nos permiten definir un
bloque básico. En los próximos apartados veremos cómo añadir configuración y
control de acceso.

Puedes consultar más información sobre creación de bloques en:

Block API
[Link]
pi/8

Definición del bloque

Para implementar un bloque crearemos un plugin de tipo Block, definiendo una


clase que extiende a \Drupal\Core\Block\BlockBase.

[Link]
BlockBase/8

Es importante tener en cuenta que, para crear un plugin de tipo Block, es necesario
craer una clase que implemente la interfaz BlockPluginInterface.

[Link]
php/interface/BlockPluginInterface/8

Para la creación de bloques disponemos de la clase intermedia BlockBase, que es


la que realmente implementa BlockPluginInterface. Esto nos permite
ahorrarnos la creación de un buen número de métodos requeridos por la interfaz,
de forma que en la clase final de nuestro bloque solo tendremos que definir los
métodos que necesitemos sobrescribir.

En esta unidad crearemos el módulo Forcontu Blocks (forcontu_blocks),


dentro del grupo Forcontu.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 5


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Como primer ejemplo, vamos a crear un bloque muy simple, que muestra un texto
fijo. Crearemos el archivo /src/Plugin/Block/[Link], donde
definiremos la clase SimpleBlock que extiende BlockBase.

<?php

namespace Drupal\forcontu_blocks\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
* Provides a Simple example block.
*
* @Block(
* id = "forcontu_blocks_simple_block",
* admin_label = @Translation("Forcontu Simple Block")
* )
*/
class SimpleBlock extends BlockBase {

public function build() {


return [
'#markup' => '<span>' . $this->t('Sample block') . '</span>'
];
}
}

Annotations

El bloque es un plugin que se registra a través de Annotations, así que el bloque


de comentarios es obligatorio y debe contener la directiva @Block. Las propiedades
que se pueden incluir dentro de esta directiva se definen en la clase
\Drupal\Core\Block\Annotation\Block:

[Link]
/class/Block/8

Las propiedades principales son:

- id. ID del plugin. Se trata de un ID único. Por norma general utilizaremos


el nombre del módulo como prefijo. En nuestro ejemplo, el id del bloque
será 'forcontu_blocks_simple_block'.

- admin_label. Etiqueta administrativa del bloque. Se corresponde con el


nombre del bloque en el listado de administración de bloques y con el
título por defecto. En nuestro ejemplo, la etiqueta será 'Forcontu Simple
Block'.

- category. Nombre de la categoría del bloque, dentro del listado de


administración. Si no se incluye esta propiedad, la categoría se
corresponderá con el nombre del módulo donde se define el bloque. En
nuestro ejemplo, como no hemos definido la propiedad, la caregoría será
Forcontu Blocks.

6 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Método build()

El método build() es el único método que tendremos que definir obligatoriamente,


ya que se exige desde la interfaz y no hay una implementación por defecto en la
clase BlockBase.

Desde build() tenemos que devolver un array renderizable, que generará el


contenido del bloque. Es una salida similar a la que ya usamos en el método
controlador de una página.

public function build() {


return [
'#markup' => '<span>' . $this->t('Sample block') . '</span>'
];
}

Colocación del bloque

Una vez instalado el módulo, el nuevo bloque se mostrará en el listado de


administración de bloques, dentro de la categoría Forcontu Blocks [F25.2a].

F25.2a
Colocar bloque
Bloque implementado
dentro del módulo
Forcontu Blocks.

En la Figura [F25.2b] se muestra el bloque colocado en una región del sitio.

F25.2b
Bloque programado
Bloque Simple block una
vez colocado en una
región del tema.

Nota: Si al intentar colocar el bloque, el botón de Colocar bloque no realiza


ninguna acción, revisa los errores en Mensajes recientes del registro. El
error no se muestra por pantalla, pero quedará registrado en el log. No olvides
añadir las cláusulas use necesarias, en función de las clases utilizadas.

Desinstalación de bloques

Los bloques implementados dentro de un módulo se eliminan cuando el módulo es


desinstalado. La configuración asociada a estos bloques también es eliminada del
sistema.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 7


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

25.3 Configuración por defecto de bloques

Al colocar el bloque, se heredan todas las opciones de configuración de bloques,


definidas para el tipo de plugin Block.

El método defaultConfiguration() permite establecer la configuración por


defecto que tendrá el bloque, y que el usuario podrá modificar al Colocarlo en una
región o desde la opción Configurar.

Las propiedades cuyos valores podemos establecer son aquellas que están
definidas en el formulario de configuración del bloque. Para ver qué propiedades
hay disponibles, consultaremos el método buildConfigurationForm() de la clase
BlockBase. En el próximo apartado veremos cómo ampliar el formulario para
añadir opciones de configuración adicionales.

[Link]
on/BlockBase::buildConfigurationForm/8

- 'label'. Permite especificar el título que se mostrará en el bloque. Si no


se define un valor personalizado, se utilizará el valor de la etiqueta
administrativa (admin_label), que hemos definido al crear el bloque (vía
Annotations).

- 'label_display'. Si se establece a FALSE, no se muestra el título del


bloque. El valor para que se muestre el título es 'visible', que no
utilizaremos directamente, sino que se lo asignaremos a través de la
constante BlockInterface::BLOCK_LABEL_VISIBLE. Para usar esta
constante, no olvides añadir la cláusula:

use Drupal\block\BlockInterface

En el siguiente ejemplo se le asigna un valor personalizado a la etiqueta o Título


(no se utilizará el nombre administrativo), pero se desactiva por defecto (Mostrar
título desactivado) [F25.3a].

public function defaultConfiguration() {


return [
'label' => 'Custom Title',
'label_display' => FALSE,
];
}

F25.3a
Valores por defecto
Valores de configuración
por defecto.

8 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Configuración personalizada de bloques 25.4

Un bloque es un plugin que, a su vez, utiliza una entidad de configuración para


almacenar toda la información relacionada con el bloque. La clase BlockBase que
utilizamos de base para construir los bloques, ya incluye toda la lógica necesaria
para gestionar esta configuración (veremos más sobre Entidades de configuración
en la Unidad 30).

En este apartado crearemos un segundo Bloque dentro del módulo Forcontu


Blocks. La clase (y archivo) del bloque se llamará HighlightedContentBlock, y
mostrará un listado con los nodos que han sido previamente marcados como
contenido Destacado (campo adicional almacenado en una tabla que añadimos en
el módulo Forcontu Forms).

Definición del bloque HighlightedContentBlock

Comenzamos definiendo el nuevo bloque, creando el archivo:

/forcontu_blocks/src/Plugin/Block/[Link]

<?php

namespace Drupal\forcontu_blocks\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
* Provides the HighlightedContent block.
*
* @Block(
* id = "forcontu_blocks_highlighted_content_block",
* admin_label = @Translation("Highlighted Content")
* )
*/
class HighlightedContentBlock extends BlockBase {

public function build() {


return [
'#markup' => '<span>' . $this->t('Highlighted') . '</span>'
];
}

Especificamos los valores de id y admin_label a través de Annotations, usando la


cláusula @Block.

Por ahora hemos añadido un método build() que devuelve una cadena. Más
adelante modificaremos este método para que imprima el listado de nodos
Destacados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 9


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Formulario de configuración del bloque: blockForm()

En el método blockForm() definiremos los elementos de formulario específicos


para el bloque, que se añadirán al formulario de configuración junto al resto de
elementos comunes.

<?php

use Drupal\Core\Form\FormStateInterface;

//...

class HighlightedContentBlock extends BlockBase {


//...

public function blockForm($form, FormStateInterface $form_state) {

$form['forcontu_blocks_block_message'] = array(
'#type' => 'textfield',
'#title' => $this->t('Display message'),
'#default_value' => $this->configuration['block_message'],
);

$range = range(1, 10);


$form['forcontu_blocks_node_number'] = array(
'#type' => 'select',
'#title' => $this->t('Number of nodes'),
'#default_value' => $this->configuration['node_number'],
'#options' => array_combine($range, $range),
);
return $form;
}
}

En la configuración de este bloque, estamos añadiendo dos campos adicionales


[F25.4a]:

- Un campo de tipo texto (textfield) que servirá de cabecera del bloque.


- Un campo de tipo select que muestra un listado de 1 a 10.

F25.4a
Formulario de
configuración
Formulario de
configuración del bloque.

Estos campos se almacenarán en las variables de configuración:

$this->configuration['block_message'],
$this->configuration['node_number'],

10 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Desde el método blockForm() solo estamos leyendo las variables de


configuración. Más adelante veremos en qué punto tenemos que almacenar los
valores modificados a través del formulario. De hecho, si modificas estos valores y
guardas la configuración del bloque, podrás comprobar, accediendo nuevamente a
la configuración, que los valores no se han almacenado.

Nota: Para construir el array de opciones #options del elemento select, hemos
utilizado las funciones de PHP range y array_combine(). Con range() construimos
un array del tipo [1, 2, 3], y con array_combine() construimos un array con formato
clave => valor, que es el formato esperado por #options:

Array
(
[1] => 1
[2] => 2
[3] => 3
)

Puedes consultar ambas funciones en la API de PHP:


[Link]
[Link]

Validación del formulario de configuración: blockValidate()

El método blockValidate() nos permitirá realizar validaciones sobre cualquier


campo del formulario de configuración.

En nuestro ejemplo comprobamos que el texto introducido tenga al menos 10


caracteres de longitud.

<?php

//...

public function blockValidate($form, FormStateInterface $form_state) {

if (strlen($form_state->getValue('forcontu_blocks_block_message')) < 10) {


$form_state->setErrorByName('forcontu_blocks_block_message',
$this->t('The text must be at least 10 characters long'));
}
}

Submit del formulario de configuración: blockSubmit()

Por último, implementamos la función blockSumbit(), que se encarga de


almacenar los valores del formulario. Solo se ejecuta blockSubmit() si el formulario
ha pasado todas las validaciones especificadas en blockValidate(), además de las
validaciones que pudiera tener el formulario base.

Solo tenemos que leer cada uno de los valores enviados en el formulario a través
de $form_state->getValue(), y asignarlos a la variable de configuración
correspondiente.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 11


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

<?php

//...

public function blockSubmit($form, FormStateInterface $form_state) {


$this->configuration['block_message'] =
$form_state->getValue('forcontu_blocks_block_message');
$this->configuration['node_number'] =
$form_state->getValue('forcontu_blocks_node_number');
}

Ahora sí podemos modificar los valores del formulario y comprobar que, accediendo
nuevamente a la configuración, los valores han quedado almacenados.

Estas variables de configuración se añaden a un grupo de configuración específico


para nuestro bloque, que tendrá el nombre:

[Link]

Si consultas esta configuración desde el Editor de configuración de Devel (o a


través de comandos de consola), verás que también se almacenan en la misma
configuración, otros valores de configuración del bloque:

uuid: ae4683ba-2e37-4fe5-822c-9122c2f209fe
langcode: es
status: true
dependencies:
module:
- forcontu_blocks
theme:
- bartik
id: highlightedcontent
theme: bartik
region: sidebar_second
weight: 0
provider: null
plugin: forcontu_blocks_highlighted_content_block
settings:
id: forcontu_blocks_highlighted_content_block
label: 'Highlighted Content'
provider: forcontu_blocks
label_display: visible
block_message: 'lorem ipsum'
node_number: '3'
visibility: { }

12 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Valor de configuración por defecto

En el apartado anterior ya vimos el método defaultConfiguration(), para dar


valores iniciales de configuración al bloque. Además de los valores de configuración
del formulario base, también podemos añadir valores por defecto para los
elementos de configuración personalizados que hemos añadido desde
blockForm() [F25.4b].

<?php

//...
public function defaultConfiguration() {
return array(
'block_message' => 'List of highlighted nodes',
'node_number' => 5,
);
}

Nótese que hacemos referencia a las variables de configuración y no a los nombres


de los campos del formulario. Ten en cuenta también que, para ver los valores de
configuración por defecto, tendrás que reinstalar el módulo.

F25.4b
Valores por defecto
Configuración por defecto
para los elementos
personalizados del
formulario de
configuración.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 13


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Configuración por defecto en archivos YAML

La configuración por defecto de un bloque también se puede establecer a nivel de


archivo, creando un formato con formato YAML en /config/install. Por ejemplo,
podemos crear un archivo llamado:

/forcontu_blocks/config/install/[Link].bartik_highlightedcontent.yml

donde incluiremos la configuración del bloque cuando se esté utilizando el tema


Bartik:

langcode: es
status: true
dependencies:
module:
- forcontu_blocks
theme:
- bartik
id: highlightedcontent
theme: bartik
region: sidebar_second
weight: 0
provider: null
plugin: forcontu_blocks_highlighted_content_block
settings:
id: forcontu_blocks_highlighted_content_block
label: 'Highlighted Content'
provider: forcontu_blocks
label_display: visible
block_message: 'lorem ipsum'
node_number: '3'
visibility: { }

Dentro del archivo de configuración por defecto hemos especificado:

- El tema del que depende el bloque (theme: bartik). Esta configuración


solo tendrá efecto sobre el tema Bartik. Si en nuestro sitio estamos usando
varios temas, podemos añadir archivos de configuración específicos,
indicando el nombre del tema en el nombre de archivo.

- El estado del bloque (status: true). Indicamos así que el bloque estará
activado por defecto.

- La región en la que se ubicará el bloque (region: sidebar_second). Por


tanto, el bloque se activará por defecto en el tema Bartik, dentro de la
región Sidebar Second (columna lateral derecha).

- En el apartado settings podemos establecer valores iniciales para el


formulario de configuración.

Esta configuración solo se tendrá en cuenta al instalar el módulo. No olvides


reinstalar el módulo para probar su funcionamiento.

14 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Inyección de servicios en plugins 25.5

Ya hemos visto cómo implementar la inyección de servicios en clases controladoras


de páginas o clases de formularios. En los plugins (y por tanto en los bloques), la
fórmula para inyectar servicios es muy parecida, pero con algunas diferencias que
detallamos a continuación. La primera gran diferencia es que la clase que instancia
el Plugin debe implementar la interfaz ContainerFactoryPluginInterface. Esta
interfaz aporta la estructura del método create():

[Link]
[Link]/interface/ContainerFactoryPluginInterface/8

Como ejemplo, vamos a inyectar en la clase HighlightedContentBlock los servicios


database y current_user.

<?php

//...
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Database\Connection;
//...
class HighlightedContentBlock extends BlockBase implements
ContainerFactoryPluginInterface {

protected $database;
protected $currentUser;

public function __construct(array $configuration,


$plugin_id,
$plugin_definition,
AccountInterface $current_user,
Connection $database) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->currentUser = $current_user;
$this->database = $database;
}
public static function create(ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_user'),
$container->get('database')
);
}
//...
}

Ten en cuenta los parámetros (y el orden) que se requieren en ambos métodos.


Recuerda que la llamada a "new static()"no es más que una llamada al constructor
de la clase actual, por lo que se deben asignar los mismos parámetros que tiene el
constructor y en el mismo orden.

Una vez inyectados, los servicios estarán disponibles en cualquiera de los métodos de
la clase, a través de las propiedades $this->currentUser y $this->database.
Contenido del bloque

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 15


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Ya solo nos queda presentar el contenido del bloque, según la configuración


establecida. Como ya vimos, es el método build() el encargado de imprimir la salida
del bloque.

Nuestro bloque mostrará un listado con los nodos "destacados", según la


información almacenada en la tabla forcontu_node_highlighted, creada en el
módulo Forcontu Forms. El número de nodos a mostrar dependerá del valor
establecido en la variable de configuración del bloque 'node_number'.

También mostraremos el mensaje almacenado en 'block_message', que se


mostrará como cabecera antes del listado de nodos.

<?php

//...
public function build() {

$node_number = $this->configuration['node_number'];
$block_message = $this->configuration['block_message'];

// 1
$build[] = [
'#markup' => '<h3>' . $this->t($block_message) . '</h3>',
];

// 2a
$result = $this->database->select('forcontu_node_highlighted', 'f')
->fields('f', ['nid'])
->condition('highlighted', 1)
->orderBy('nid', 'DESC')
->range(0, $node_number)
->execute();

// 2b
$list = [];
$node_storage = \Drupal::entityTypeManager()->getStorage('node');

// 2c
foreach($result as $record) {
$node = $node_storage->load($record->nid);
$list[] = $node->toLink($node->getTitle())->toRenderable();
}

if (empty($list)) {
$build[] = [
'#markup' => '<h3>' . $this->t('No results found') . '</h3>',
];
} else {
// 2d
$build[] = [
'#theme' => 'item_list',
'#items' => $list,
'#cache' => ['max-age' => 0],
];
}

return $build;
}

Comentamos paso a paso el código implementado:

- 1. En el array $build definiremos varios elementos renderizables. Al no


tratarse de un formulario, no hemos incluido un índice para cada elemento
($build[]), pero se podrían añadir.

El primer elemento es un texto (markup) que muestra el valor almacenado


en la variable de configuración 'block_message'.

16 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

- 2a. Se realiza la consulta a la base de datos para obtener el listado de


nodos destacados. Como comentamos, esta información se almacena en
la tabla forcontu_node_highlighted, creada en el módulo Forcontu Forms.
El número de nodos a obtener se especifica a través del método range(),
pasando como valor la vriable 'node_number' de la configuración del
bloque.

- 2b. Para trabajar con los nodos (y otras entidades), utilizamos la clase
EntityTypeManager, que estudiaremos con más profundidad en próximas
unidades. En primer lugar se obtiene el almacén de objetos
($node_storage), que nos permitirá obtener nodos.

Nótese que la forma correcta de utilizar la clase entityTypeManager es


mediante la inyección del servicio entity_type.manager. Lo dejaremos
para una de las actividades propuestas en esta unidad.

- 2c. Cargamos cada nodo obtenido ($node_storage->load()) y generamos


un enlace con el título del nodo. Cada valor se almacena en un array
$list[].

- 2d. Se crea el elemento renderizable con la plantilla 'item_list', que genera


un listado de elementos (<ul><li>). El array $list se pasa como valor del
atibuto '#items'. Aquí hemos añadido el atributo #cache para evitar que
el contenido se cachee. Veremos más sobre caché de elementos en
próximas unidades.

El resultado se muestra en la Figura [F25.5a]. Edita algunos nodos del sitio y


márcalos como Destacados para que se muestren en el bloque. Modifica también
los valores de 'node_number' y 'block_message' desde el formulario de
configuración del bloque.

F25.5a
Presentación del
bloque
Listado de nodos
destacados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 17


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

25.6 Control de acceso a bloques

Control de acceso: blockAccess()

Añadiendo el método blockAccess() podemos indicar si el bloque será mostrado o


no, generalmente en función de los permisos que tenga el usuario. Es por ello que
el método recibe como parámetro el objeto $account con el usuario actual.

blockAccess() devuelve un objeto de tipo AccessResult:

[Link]
ass/AccessResult/8

Uno de los métodos más utilizados es AccessResult::allowedIfHasPermission(), que


comprueba si el usuario tiene un permiso determinado:

[Link]
nction/AccessResult::allowedIfHasPermission/8

<?php

//...
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;

//...
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIfHasPermission($account, 'access content');
}

Como ya hemos estudiado, también podemos añadir un permiso específico a


nuestro módulo y utilizarlo para controlar el acceso al bloque.

18 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Integrar un formulario en un bloque 25.7

En este apartado hablamos de formularios que se muestran al usuario final, y no


de formularios de configuración. Ejemplos de este tipo de bloques con formularios
son el bloque de búsqueda y el bloque de login de usuario.

En el apartado 24.7 (Obteniendo el formulario sin ruta) ya comentamos cómo


obtener un formulario sin necesidad de cargarlo desde una página. Será el mismo
método que utilizaremos para integrar un formulario dentro de un bloque.

Los pasos para implementar un bloque de formulario son:

1. Crear el formulario.
2. Crear el bloque y renderizar el formulario con FormBuilder.

Lo interesante de este modelo es que toda la gestión del formulario se realiza en


la clase de formulario, dejando el bloque únicamente como elemento encapsulador.
Esto implica también que un mismo formulario puede utilizarse en diferentes
elementos del sitio, como una página y un bloque, sin necesidad de realizar
cambios en la clase de formulario.

Como ejemplo, vamos a crear un formulario que se mostrará en forma de bloque


cuando se esté presentando un nodo. El formulario permitirá a los usuarios votar
el contenido, indicando un valor de 1 a 5 en función de cómo de útil le haya
resultado la información.

La información enviada a través del formulario se almacenará en la tabla


forcontu_node_votes.

En la Figura [25.7a] se muestra el bloque de formulario que vamos a implementar.

F25.7a
Bloque de formulario
Este formulario se
mostrará en todas las
páginas de tipo nodo.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 19


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Paso 1. Crear el formulario

Comenzamos definiendo el formulario NodeVotingForm en el archivo:

/forcontu_blocks/src/Form/[Link]

Necesitaremos los servicios database, current_user y current_route_match,


así que los inyectamos en la clase de formulario.

<?php

namespace Drupal\forcontu_blocks\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Routing\RouteMatchInterface;

class NodeVotingForm extends FormBase {

protected $database;
protected $currentUser;
protected $currentRouteMatch;

public function __construct(Connection $database,


AccountInterface $current_user,
RouteMatchInterface $current_route_match) {
$this->database = $database;
$this->currentUser = $current_user;
$this->currentRouteMatch = $current_route_match;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('database'),
$container->get('current_user'),
$container->get('current_route_match')
);
}

public function getFormId() {


return 'forcontu_blocks_node_voting_form';
}
}

Para la presentación del formulario implementamos el método buildForm().


Comprobaremos que el usuario está autenticado y que la página actual es de tipo
node.

Consultaremos en la tabla 'forcontu_node_votes' si existe una votación emitida por


el usuario actual para el nodo que se está cargando y, si es así, se presentará como
valor por defecto del elemento de formulario (node_vote). Por tanto, el usuario
puede cambiar su voto en cualquier momento.

Los valores de nid y uid los pasamos ocultos en un campo de tipo 'value', aunque
también se podrían volver a leer en el submit del formulario desde los servicios
correspondientes.

20 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

<?php

public function buildForm(array $form, FormStateInterface $form_state) {

$node_vote = NULL;
$node = $this->currentRouteMatch->getParameter('node');

$nid = $node ? $node->id() : NULL;

if($nid && $this->currentUser->isAuthenticated()){


$node_vote = $this->database->select('forcontu_node_votes', 'f')
->fields('f', ['vote'])
->condition('[Link]', $nid)
->condition('[Link]', $this->currentUser->id())
->execute()
->fetchField();

$form['node_vote'] = [
'#type' => 'radios',
'#title' => $this->t('Vote this node'),
'#options' => [ 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5],
'#description' => $this->t('How useful did you find this content?'),
'#required' => TRUE,
'#default_value' => $node_vote,
];
$form['nid'] = [
'#type' => 'value',
'#value' => $nid,
];
$form['uid'] = [
'#type' => 'value',
'#value' => $this->currentUser->id(),
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Vote'),
];

return $form;
}
}

En la función de submit del formulario, submitForm(), almacenamos la información


enviada en el formulario. Utilizaremos el método upsert() para crear o editar el
registro, en función de si ya existe en la tabla.

<?php

public function submitForm(array &$form, FormStateInterface $form_state) {

$nid = $form_state->getValue('nid');
$uid = $form_state->getValue('uid');
$node_vote = $form_state->getValue('node_vote');

$upsert = $this->database->upsert('forcontu_node_votes')
->key('nid')
->fields(['nid', 'uid', 'vote'])
->values([
'nid' => $nid,
'uid' => $uid,
'vote' => $node_vote,
])->execute();

drupal_set_message($this->t('Your vote on this node has been registered.'));


}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 21


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Nota: Nuestra tabla tiene una clave primaria compuesta por los campos nid y uid.
Aunque upsert solo permite un valor de campo para indicar la clave primaria,
key('nid'), funcionará correctamente para nuestro caso. Esto es así porque,
internamente, la sentencia generada por Upsert tiene el formato:

INSERT… ON DUPLICATE KEY UPDATE

[Link]

Lo que hace esta sentencia es realizar el UPDATE siempre que se produzca un error
de clave duplicada tras intentar hacer el INSERT.

Crear la tabla

Por último, definiremos la tabla implementando hook_schema() en el archivo


forcontu_blocks.install. La tabla tendrá los campos nid, uid y vote (con la votación
indicada).

<?php

/**
* Implements hook_schema().
*/
function forcontu_blocks_schema() {

$schema['forcontu_node_votes'] = [
'description' => 'Stores Node votes',
'fields' => [
'nid' => [
'type' => 'int',
'not null' => TRUE,
'description' => 'Node ID',
],
'uid' => [
'type' => 'int',
'not null' => TRUE,
'description' => 'User ID',
],
'vote' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
'description' => '1 to 5',
],
],
'primary key' => ['nid', 'uid'],
];

return $schema;
}

22 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Paso 2. Crear el bloque

En Drupal 8 tenemos disponible el servicio FormBuilder (form_builder), que nos


permite cargar cualquier formulario haciendo una referencia directa a la clase que
lo construye.

$form = \Drupal::formBuilder()->getForm('Drupal\forcontu_blocks\Form\CustomForm');

Lo que devuelve getForm() es un array con la definición del formulario, que


podemos devolver directamente en el método build() del bloque.

Si el formulario tiene parámetros adicionales en su método buildForm(), también


los podemos pasar como parámetros adicionales de getForm().

En el ejemplo anterior hemos llamado directamente a \Drupal::formBuilder(), pero


también podemos inyectar el servicio 'form_builder' en la clase del bloque.
A continuación, definimos el bloque NodeVotingBlock en el archivo:
/forcontu_blocks/src/Plugin/Block/[Link]

<?php

namespace Drupal\forcontu_blocks\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormBuilderInterface;

/**
* Provides the NodeVoting block.
*
* @Block(
* id = "forcontu_blocks_node_voting_block",
* admin_label = @Translation("Node voting")
* )
*/
class NodeVotingBlock extends BlockBase implements
ContainerFactoryPluginInterface {

protected $currentRouteMatch;
protected $formBuilder;

public function __construct(array $configuration,


$plugin_id,
$plugin_definition,
RouteMatchInterface $current_route_match,
FormBuilderInterface $form_builder) {

parent::__construct($configuration, $plugin_id, $plugin_definition);


$this->currentRouteMatch = $current_route_match;
$this->formBuilder = $form_builder;
}

public static function create(ContainerInterface $container,


array $configuration,
$plugin_id,
$plugin_definition) {
return new static(
$configuration,
$plugin_id,

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 23


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

$plugin_definition,
$container->get('current_route_match'),
$container->get('form_builder')
);
}

protected function blockAccess(AccountInterface $account) {

$node = $this->currentRouteMatch->getParameter('node');
if($node && $account->isAuthenticated()) {
return AccessResult::allowed();
} else {
return AccessResult::forbidden();
}
}

public function build() {


$form =
$this->formBuilder->getForm('Drupal\forcontu_blocks\Form\NodeVotingForm');
return $form;
}
}

En este bloque hemos inyectado los servicios current_route_match, para


comprobar si la página actual es un nodo, y form_builder, para obtener el
formulario.

Desde blockAccess() comprobamos si la página es de tipo node y si el usuario


está autenticado. Si es así, el bloque se mostrará (AccessResult::allowed()). En
caso contrario, se prohibirá el acceso (AccessResult::forbidden()).

Para comprobar el funcionamiento, activa el bloque en la columna derecha del


tema. A continuación, accede a varios nodos con diferentes usuarios, y vota en
ellos. Prueba también a editar alguna de las votaciones.

24 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Modificación de bloques de otros módulos 25.8

El módulo Block aporta funciones hook que permiten modificar los bloques creados
por otros módulos. Todos estos hooks se implementarán en el archivo .module.

hook_block_view_alter() y hook_block_build_alter()

Ambas funciones permiten modificar la salida del método build() del formulario
($build). La diferencia entre ellas es que hook_block_build_alter() es llamada antes
del ensamblado del bloque, lo que permite modificar, principalmente, opciones de
caché (#cache).

hook_block_view_alter(array &$build,
\Drupal\Core\Block\BlockPluginInterface $block)

[Link]
_block_view_alter/8

hook_block_build_alter(array &$build,
\Drupal\Core\Block\BlockPluginInterface $block)

[Link]
_block_build_alter/8

A continuación se muestra un ejemplo de implementación, donde modificamos el


bloque 'system_powered_by_block' para que se muestre la etiqueta.

<?php

use Drupal\block\BlockInterface;

/**
* Implements hook_block_view_alter().
*/
function forcontu_blocks_block_view_alter(array &$build,
\Drupal\Core\Block\BlockPluginInterface $block) {

if($block->getPluginId() == 'system_powered_by_block') {
$build['#configuration']['label_display'] =
BlockInterface::BLOCK_LABEL_VISIBLE;
}
}

Las funciones anteriores se ejecutan para todos los bloques, de ahí que tengamos
que comprobar el ID del bloque antes de realizar modificaciones.

Para ambas funciones disponemos de alternativas que permiten implementar


funciones específicas para un bloque o grupo de bloques.

hook_block_view_BASE_BLOCK_ID_alter(array &$build,
\Drupal\Core\Block\BlockPluginInterface $block)

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 25


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

[Link]
_block_view_BASE_BLOCK_ID_alter/8

hook_block_build_BASE_BLOCK_ID_alter(array &$build,
\Drupal\Core\Block\BlockPluginInterface $block)

[Link]
_block_build_BASE_BLOCK_ID_alter/8

A continuación, mostramos el ejemplo anterior pero utilizando una función


específica para el bloque.

/**
* Implements hook_block_view_BASE_BLOCK_ID_alter()
* for 'system_powered_by_block'.
*/
function forcontu_blocks_block_view_system_powered_by_block_alter(
array &$build,
\Drupal\Core\Block\BlockPluginInterface $block) {

$build['#configuration']['label_display'] =
BlockInterface::BLOCK_LABEL_VISIBLE;
}

hook_block_access()

Mediante la implementación de hook_block_access() podemos añadir


restricciones de acceso a cualquier bloque del sitio.

hook_block_access(\Drupal\block\Entity\Block $block,
$operation,
\Drupal\Core\Session\AccountInterface $account)

[Link]
_block_access/8

En la siguiente implementación de hook_block_access() se impide que el bloque


"Powered by Drupal" se muestre en una región que no sea el footer.

<?php

/**
* Implements hook_block_access()
*/
function forcontu_blocks_block_access(\Drupal\block\Entity\Block $block,
$operation,
\Drupal\Core\Session\AccountInterface $account) {

if ($operation == 'view' &&


$block->getPluginId() == 'system_powered_by_block') {
return AccessResult::forbiddenIf($block->getRegion() != 'footer')-
>addCacheableDependency($block);
}

// No opinion.
return AccessResult::neutral();
}

26 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

Hazlo desde la consola 25.9

Crear un bloque (Drupal Console)

drupal generate:plugin:block

Genera un plugin de tipo bloque.

drupal generate:plugin:block options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:plugin:block

// Welcome to the Drupal Plugin Block generator


Enter the module name [admin_toolbar]:
> forcontu_console

Enter the plugin class name [DefaultBlock]:


> SimpleTestBlock

Enter the plugin label [Simple test block]:


>

Enter the plugin id [simple_test_block]:


> forcontu_console_simple_test_block

Enter the theme region to render the Plugin Block. [ ]:


> sidebar_second

Do you want to load services from the container (yes/no) [no]:


> yes

Type the service name or use keyup or keydown.


This is optional, press enter to continue
Enter your service [ ]:
> database
Enter your service [ ]:
> form_builder
Enter your service [ ]:
>

You can add input fields to create special configurations in the block.
This is optional, press enter to continue

Do you want to generate a form structure? (yes/no) [yes]:


> no
Do you confirm generation? (yes/no) [yes]:
> yes
// cache:rebuild
Rebuilding cache(s), wait a moment please.

[OK] Done clearing cache(s).

Generated or updated files

1 -
modules/custom/forcontu_console/src/Plugin/Block/[Link]

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 27


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

A continuación, se muestra el código del bloque generado por Drupal Console. El


comando también permite la creación del formulario de configuración del bloque.

<?php

namespace Drupal\forcontu_console\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\webprofiler\Form\FormBuilderWrapper;

/**
* Provides a 'SimpleTestBlock' block.
*
* @Block(
* id = "forcontu_console_simple_test_block",
* admin_label = @Translation("Simple test block"),
* )
*/
class SimpleTestBlock extends BlockBase implements
ContainerFactoryPluginInterface {

/**
* Drupal\Core\Database\Driver\mysql\Connection definition.
*
* @var \Drupal\Core\Database\Driver\mysql\Connection
*/
protected $database;
/**
* Drupal\webprofiler\Form\FormBuilderWrapper definition.
*
* @var \Drupal\webprofiler\Form\FormBuilderWrapper
*/
protected $formBuilder;
/**
* Construct.
*
* @param array $configuration
* A configuration array containing information about the
plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param string $plugin_definition
* The plugin implementation definition.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
Connection $database,

FormBuilderWrapper $form_builder
) {
parent::__construct($configuration, $plugin_id,
$plugin_definition);
$this->database = $database;
$this->formBuilder = $form_builder;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container,
array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,

28 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 25. Plugins I: Introducción a plugins y bloques

$plugin_definition,
$container->get('database'),
$container->get('form_builder')
);
}
/**
* {@inheritdoc}
*/
public function build() {
$build = [];
$build['forcontu_console_simple_test_block']['#markup'] =
'Implement SimpleTestBlock.';

return $build;
}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 29


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

26 Usuarios y permisos
El control de acceso en Drupal se basa en roles y permisos. Un
Comparativa D8/D7
módulo puede definir varios permisos, que el administrador podrá
asociar con los roles creados en el sitio. Los usuarios podrán tener Programación de usuarios y permisos
asignados uno o varios roles, lo que implica que, indirectamente, En Drupal 8 se mantienen algunos hooks
tienen asociados una serie de permisos. similares a los empleados en Drupal 7. El resto
se puede considerar totalmente nuevo y
requiere un estudio en profundidad.
Ya hemos visto cómo acceder al usuario actual. En esta unidad
veremos también cómo gestionar entidades de tipo User, crear roles
y permisos, añadir información adicional al usuario, etc.

Contenidos de la Unidad
26.1 Obteniendo el usuario actual

26
26.2 Roles, permisos y control de acceso
26.3 La entidad User
26.4 Operaciones CRUD y hooks de Entity API
26.5 Hooks del módulo User
26.6 Información adicional de usuario
26.7 Otras clases y servicios relacionados con usuarios
26.8 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 33


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

26.1 Obteniendo el usuario actual

En unidades anteriores ya hemos utilizado el servicio que nos proporciona el


usuario actual.

Servicio: current_user
Clase: Drupal\Core\Session\AccountProxy

[Link]
class/AccountProxy/8

Aunque podemos acceder directamente al contenedor de servicios con la clase


Drupal:

$account = \Drupal:currentUser();

siempre que sea posible, inyectaremos el servicio 'current_user' en la clase que


estemos implementando (controlador, formulario, plugin, etc.). Dentro de la clase
donde hayamos inyectado el servicio, podremos utilizarlo directamente a través de
$this->currentUser.

Veamos algunos de los métodos disponibles en la clase AccountProxy, utilizada por


el servicio current_user:

- getAccountName(). Devuelve el nombre de usuario que es utilizado


para hacer login con esta cuenta.

- getDisplayName(). Devuelve el nombre de usuario que se muestra en


el sitio. Inicialmente se corresponde con el valor de la propiedad 'name'
del usuario, o el valor 'anonymous' si no es un usuario registrado, pero
puede modificarse a través del hook_user_format_name_alter(), como
veremos en próximos apartados.

- getEmail(). Devuelve el email del usuario.

- getLastAccessedTime(). Devuelve la fecha, en formato timestamp, del


último acceso del usuario.

- getRoles(). Devuelve un array con los roles asignados al usuario. Se


puede pasar como parámetro $exclude_locked_roles = TRUE para que se
omitan los roles anonymous y authenticated.

- id(). Devuelve el ID de usuario, o 0 si el usuario es anónimo.

- isAnonymous(). Devuelve TRUE si se trata de un usuario anónimo.

- isAuthenticated(). Devuelve TRUe si se trata de un usuario autenticado.

- hasPermission($permission). Comprueba si el usuario tiene el permiso

34 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

$permission, devolviendo TRUE en caso afirmativo o FALSE en caso


contrario.

Esta clase solo nos permite consultar información relacionada con el usuario actual,
pero no modificarla. En los próximos apartados veremos cómo cargar el objeto
User y realizar otras operaciones. También veremos cómo almacenar y recuperar
información adicional relacionada con los usuarios.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 35


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

26.2 Roles, permisos y control de acceso

Definición de permisos

El control de acceso en Drupal se basa en roles y permisos. Un módulo puede


definir varios permisos, que el administrador podrá asociar con los roles creados
en el sitio. Los usuarios podrán tener asignados uno o varios roles, lo que implica
que, indirectamente, tienen asociados una serie de permisos.

Los permisos del módulo se definen en el archivo .[Link], en la carpeta


raíz del módulo.

En esta unidad vamos a crear el módulo Forcontu Users (forcontu_users), donde


crearemos el archivo forcontu_users.[Link], con los siguientes
permisos:

'access user block':


title: View user block
description: View own user block

'configure extra user data':


title: Configure extra user data
description: Allow users to configure extra user data

Estos permisos se mostrarán en el listado de permisos:

/admin/people/permissions

F26.2a
Permisos
Permisos definidos para el
módulo Forcontu Users.

Control de acceso básico

El método hasPermission($permission), disponible en el servicio current_user, es


uno de los métodos que tenemos a nuestra disposición para controlar el acceso. El
método comprobará si el usuario actual tiene el permiso indicado en $permission,
devolviendo TRUE en caso afirmativo o FALSE en caso contrario.

En el apartado 18.8, ya vimos un ejemplo de utilización del método


hasPermission() dentro de una página. En este caso, el acceso a la página es
público, pero parte del contenido solo se mostrará si el usuario actual tiene un
permiso determinado:

36 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

<?php

//file: forcontu_pages/src/Controller/[Link]
//...
public function tab1() {

$output = '<p>' . $this->t('This is the content of Tab 1') . '</p>';

if($this->currentUser->hasPermission('administer nodes')){
$output .= $this->t('This extra text is only displayed if the
current user can administer nodes.') . '</p>';
}
return array(
'#markup' => $output,
);
}
//...
}

Cuando corresponda, también hacer uso de los métodos isAnonymous() y


isAuthenticated().

Control de acceso a rutas

En el apartado 18.7 vimos que, para controlar el acceso a una ruta, debemos
especificar el permiso en la definición de la ruta (archivo .[Link]). Por
ejemplo, en el módulo system del núcleo, se definen las siguietes rutas con sus
correspondientes permisos de acceso (archivo /core/modules/[Link]):

[Link]:
path: '/admin'
defaults:
_controller:
'\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
_title: 'Administration'
requirements:
_permission: 'access administration pages'

system.admin_structure:
path: '/admin/structure'
defaults:
_controller:
'\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
_title: 'Structure'
requirements:
_permission: 'access administration pages'

system.admin_reports:
path: '/admin/reports'
defaults:
_controller:
'\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
_title: 'Reports'
requirements:
_permission: 'access site reports'

Permisos de otros módulos

Para utilizar permisos de otros módulos, necesitamos conocer el nombre de


sistema. Estos nombres de sistema de permisos se pueden localizar en el
archivo .[Link] de cada módulo.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 37


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

Por ejemplo, el permiso 'administer site configuration', es un permiso del


módulo system del núcleo, y, por tanto, encontraremos su definición en el archivo
/core/modules/system/[Link].

Como vimos en el apartado 16.5, también podemos activar la opción Muestra


el nombre de sistema de permisos y módulos desde la configuración del
módulo Devel. Activando esta opción, en las páginas de administración de permisos
y módulos, se mostrará el nombre de sistema de cada elemento al pasar el ratón
por encima.

Control de acceso por rol

En este ejemplo hemos utilizado el control de acceso por permiso, que es el más
habitual, pero también se podría realizar por rol:

requirements:
_role: 'gestor'

Ruta sin control de acceso

Cuando una ruta es de acceso público, o vamos a utilizar otro método de validación,
usaremos la directiva _access a TRUE:

requirements:
_access: 'TRUE'

Control de acceso personalizado

Para controlar el acceso a rutas, también podemos crear un método o función


personalizado para comprobar si el usuario tiene acceso. Usaremos la propiedad
_custom_access indicando el nombre de la clase y el método correspondiente.

requirements:
_custom_access: '\Drupal\forcontu_forms\Form\Simple::access'

A continuación, se muestra un ejemplo de método de control de acceso


personalizado, que hemos definido en la misma clase que controla un formulario
(apartado 24.5). Dentro de la función realizaremos todas las comprobaciones
necesarias, devolviendo un objeto de tipo AccessResult.

<?php

use Drupal\Core\Access\AccessResult;

//...

public function access(AccountInterface $account) {


return AccessResult::allowedIf($account->hasPermission('forcontu
form access') && $account->hasPermission('administer site
configuration'));
}

38 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

AccessResult

Todos los métodos en los que se controle el acceso a algún elemento, deben
devolver un objeto AccessResultInterface. La clase utilizada es AccessResult, que
también implementa la interfaz RefinableCacheableDependencyInterface.

[Link]
ass/AccessResult/8

Utilizamos AccessResult, en lugar de un simple TRUE/FALSE, para poder añadir


información adicional (metadata) relacionada con cómo se debe cachear el control
de acceso a un elemento.

Por ejemplo, podemos establecer que el control de acceso a un bloque se cachee


en función del usuario, o que el control de acceso a una ruta se cachee según el
rol y el idioma de la interfaz.

Es importante entender que lo que estamos estableciendo aquí es cómo se debe


cachear el resultado de acceso (AccessResult), y no el objeto en sí. Por ejemplo, si
el acceso a un bloque se establece por usuario, la primera vez que se cargue el
bloque para un usuario determinado, se comprobará si el usuario tiene acceso. Esa
validación se almacena en caché, de forma que cuando se tenga que volver a
comprobar si el usuario tiene acceso al bloque, el sistema consultará esta
información directamente desde la caché.

Algunos de los métodos disponibles en AccessResult:

- allowed(). Devuelve un objeto de tipo AccessResultInterface (clase


AccessResultAllowed), con isAllowed() === TRUE. Indica que el
acceso está permitido.

- forbidden(). Devuelve un objeto de tipo AccessResultInterface (clase


AccessResultForbidden), con isForbidden() === TRUE. Indica que el
acceso está prohibido.

- neutral(). Devuelve un objeto de tipo AccessResultInterface (clase


AccessResultNeutral), con isNeutral() === TRUE. Deja el acceso como
no determinado, sin posicionarse a favor (permitir) o en contra (prohibir).

// Los usuarios anónimos no tienen acceso


if ($account->isAnonymous()) {
return AccessResult::forbidden();
}

- isAllowed(). Comprueba si el acceso está permitido de forma explícita.

- isForbidden(). Comprueba si el acceso está prohibido de forma explícita.

- isNeutral(). Comprueba si el acceso aún no se ha determinado (no está


ni permitido ni rechazado).

- allowedIf($condition). Si se cumple la condición, crea un AccessResult

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 39


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

con acceso permitido. En caso contrario, crea un objeto neutral.

return AccessResult::allowedIf($operation !== 'edit');

- allowedIfHasPermission($account, $permission). Comprueba si la


cuenta tiene el permiso indicado. Crea un AccessResult con acceso
permitido cuando se cumple la condición, y neutral en caso contrario.

return AccessResult::allowedIfHasPermission($account, 'access news');

- allowedIfHasPermissions($account, array $permissions, $conjunction


= 'AND'). Permite comprobar varios permisos. La comprobación por
defecto es AND, con lo que la cuenta de usuario debe tener todos los
permisos requeridos. Utilizando OR, se dará acceso al usuario cuando
tenga al menos uno de los permisos del array $permissions.

return AccessResult::allowedIfHasPermissions($account, ['create


url aliases', 'administer url aliases'], 'OR');

- forbiddenIf($condition). Prohíbe el acceso cuando se cumple la


condición. En caso contrario se devuelve un resultado neutral.

En el siguiente ejemplo, el bloque 'system_powered_by_block' no se


muestra si se intenta colocar en una región diferente a 'footer'.

if ($operation == 'view' && $block->getPluginId() == 'system_powered_by_block') {


return AccessResult::forbiddenIf($block->getRegion() != 'footer');
}

- andIf(AccessResultInterface $other). Permite combinar el AccessResult


actual con otro ($other), utilizando un operador AND.

o Si cualquiera de los dos devuelve Prohibido, el resultado es


Prohibido.
o Si los dos devuelven Permitido, el resultado es Permitido.
o Si uno devuelve Permitido, pero el otro devuelve Neutral, el
resultado es Neutral. También devolverá Neutral cuando los dos
devuelven Neutral.

- orIf(AccessResultInterface $other). Permite combinar el AccessResult


actual con otro ($other), utilizando un operador OR.

o Si cualquiera de los dos devuelve Prohibido, el resultado es


Prohibido.
o Si los dos devuelven Permitido o uno devuelve Permitido y el otro
Neutral, el resultado es Permitido.
o Si los dos devuelven Neutral, el resultado es Neutral.

En el siguiente ejemplo se comprueba si el usuario tiene el permiso 'post


comments'. Luego se evalúa el valor de $pid, y en caso de que se cumpla
la condición, se comprueba también (andIf()) que el usuario tenga el
permiso 'access comments'.

40 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

$access = AccessResult::allowedIfHasPermission($account, 'post comments');

if ($pid) {
$access = $access->andIf(AccessResult::allowedIfHasPermission($account,
'access comments'));
}

return $access;

Como ya hemos comentado, el objeto AccessResult también puede llevar


información de caché asociada a la validación. Algunos de los métodos disponibles
son:

- setCacheMaxAge($max_age). Establece el tiempo máximo de caché del


AccessResult. El valor de $max_age se indica en segundos.

El valor por defecto de la propiedad $this->cacheMaxAge es


Cache::PERMANENT, con lo que la caché se almacena de forma
permanente y solo se vacía al limpiar la caché del sitio.

En el siguiente ejemplo, se establece un valor a 0, indicando que el


resultado de la validación no será cacheable.

if ($some_condition) {
$result = AccessResult::allowed();
} else {
$result = AccessResult::forbidden();
}

// Not cacheable
return $result->setCacheMaxAge(0);

- getCacheMaxAge(). Devuelve el tiempo de caché para este objeto de


validación, disponible en la propiedad $this->cacheMaxAge.

- addCacheContexts(). Añade contextos o condiciones adicionales de


caché.

- cachePerPermissions(). Añade el contexto de caché '[Link]',


indicando que el resultado de la validación AccessResult se puede cachear
por permisos.

- cachePerUser(). Añade el contexto de caché 'user', indicando que el


resultado de la validación AccessResult se puede cachear por usuario.

return AccessResult::allowed()->cachePerUser();

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 41


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

Control de acceso a bloques

Otro ejemplo de uso de AccessResult lo vimos en el control de acceso a bloques, a


través del método blockAccess (apartado 25.6).

<?php

//...
use Drupal\Core\Access\AccessResult;

//...
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIfHasPermission($account, 'access content');
}

42 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

La entidad User 26.3

Clase User

La clase User implementa la intefaz UserInterface para definir una entidad de tipo
usuario.

[Link]
r/8

En próximas unidades veremos cómo se crean los tipos de entidades, tipos de


campos, y otros elementos relacionados. En esta unidad nos centramos en la clase
User y los métodos que tenemos disponibles.

Cargar el usuario actual

En el apartado 26.1 vimos cómo acceder al usuario actual. Es objeto devuelto por
el servicio current_user no es la entidad user en sí, así que las operaciones que
podemos realizar sobre él están limitadas.

Para cargar la entidad user utilizaremos el método load(), pasando como parámetro
el id() del usuario actual:

use Drupal\user\Entity\User;

$user = User::load(\Drupal::currentUser()->id());

Métodos de la clase User

Veamos ahora algunos de los métodos disponibles para trabajar con el objeto User:

- id(). Devuelve el ID de usuario (uid). También puede devolver NULL,


cuando el usuario es nuevo (se está creando en ese momento).

- get(), set(). Obtener y establecer valores de campos.

- getLastLoginTime(). Devuelve la fecha, en formato timestamp, del


último login del usuario.

- getAccountName(). Devuelve el nombre de usuario que es utilizado


para hacer login con esta cuenta.

- getDisplayName(). Devuelve el nombre de usuario que se muestra en


el sitio. Inicialmente se corresponde con el valor de la propiedad 'name'
del usuario, o el valor 'anonymous' si no es un usuario registrado, pero
puede modificarse a través del hook_user_format_name_alter(), como
veremos en próximos apartados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 43


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

- getEmail(). Devuelve el email del usuario., setEmail().

- getRoles(). Devuelve un array con los roles asignados al usuario. Se


puede pasar como parámetro $exclude_locked_roles = TRUE para que se
omitan los roles anonymous y authenticated.

- hasPermission($permission). Comprueba si el usuario tiene el permiso


$permission, devolviendo TRUE en caso afirmativo o FALSE en caso
contrario.

- isAnonymous(). Devuelve TRUE si se trata de un usuario anónimo.

- isAuthenticated(). Devuelve TRUe si se trata de un usuario autenticado.

- isBlocked(), isActive(). Comprueba si el usuario está bloqueado o


activo, respectivamente.

- isNew(). Comprueba si el usuario es nuevo (se está creando en ese


momento).

- activate(), block(). Activa o bloquea al usuario, respectivamente.

- addRole($rid), removeRole($rid). Añade o elimina al usuario el rol $rid


especificado.

- hasRole($rid). Comprueba si el usuario tiene el rol $rid (ID del rol).

En el próximo apartado veremos otros métodos de la clase User para realizar


operaciones como crear, editar, guardar o eliminar usuarios.

Estructura de la base de datos

La entidad User implementa las tablas users, que es la tabla base donde se
almacenan los usuarios, y users_field_data, donde se almacena la información
de cada usuario.

- users. Es la tabla base para las entidades de tipo User.


o uid. Id del usuario. Se corresponde con el valor devuelto por el
método id().
o uuid.
o langcode.

- users_field_data. Es la tabla donde se almacenan los datos de la


entidad User.
o uid.
o langcode.
o preferred_langcode.
o preferred_admin_langcode.
o name.
o pass.
o mail.

44 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

o timezone.
o status.
o created.
o changed.
o access.
o login.
o init. Email original, utilizado al crear la cuenta.
o default_langcode.

Otras tablas relacionadas con los usuarios son:

- users_data. Tabla que permite que otros módulos almacenen


información adicional de los usuarios (apartado 26.6).
o uid.
o module.
o name.
o value
o serialized.

- users__roles. Almacena los roles disponibles en el sitio.


o bundle.
o deleted.
o entity_id.
o revision_id.
o langcode.
o delta.
o roles_target_id.

- users__user_picture. Almacena la información de la imagen de perfil


del usuario. Al tratarse de un archivo, se relaciona con las tablas
file_managed y file_usage, que estudiaremos más adelante.
o bundle.
o deleted.
o entity_id.
o revision_id.
o langcode.
o delta.
o user_picture_target_id. -> file_managed y file_usage.
o user_picture_alt.
o user_picture_title.
o user_picture_width.
o user_picture_height.

Accede a la base de datos y comprueba los valores almacenados en estas tablas.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 45


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

26.4 Operaciones CRUD y hooks de Entity API

En este apartado vamos a estudiar las clases, métodos y hooks que nos permiten
realizar operaciones CRUD (crear, leer, actualizar y eliminar) sobre entidades de
tipo User. Puedes leer más sobre estas operaciones en:

[Link]
entity_crud/8

EntityTypeManager

La clase EntityTypeManager, a través del servicio entity_type.manager, nos


permite gestionar cualquier tipo de entidad.

Servicio: entity_type.manager
Clase:
[Link]
hp/class/EntityTypeManager/8

Esta clase es fundamental para cargar entidades, accediendo primero al almacén


(storage) de un tipo de entidad determinado:

$user_storage = \Drupal::entityTypeManager()->getStorage('user');

Como en otros muchos casos, podemos utilizar el servicio desde la clase global
\Drupal, como en el ejemplo anterior, o inyectando el servicio en la clase en que
estemos trabajando.

El almacén es un objeto que implementa EntityStorageInterface, añadiendo una


serie de métodos para realizar operaciones CRUD sobre entidades:

[Link]
[Link]/interface/EntityStorageInterface/8

- load($id). Carga una entidad identificada por su ID.

$admin = $user_storage->load(1);

- loadMultiple(array $ids). Carga varias entidades según los IDs


especificados en el array $ids. Devuelve un array de entidades, indexadas
por su ID. Si no se pasa un array como parámetro (NULL), se cargan todas
las entidades.

$selected_users = [4, 13, 27];


$accounts = $user_storage->loadMultiple($selected_users);

- loadByProperties(array $values). Carga varias entidades filtrando por


las propiedades especificadas en el array $values.

46 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

$accounts = $user_storage->loadByProperties(['name' => $username,


'status' => 1]);

- create(array $values). Crea un objeto de entidad, sin guardarlo. En


nuestro caso crea un nuevo usuario, al que añadiremos propiedades en
el array $values.

- save($entity). Guarda permanentemente la entidad.

$new_user = $user_storage->create([
'name' => 'test_user',
'mail' => 'foo@[Link]',
'pass' => '123456',
]);

$new_user->save();

En este ejemplo se crea un usuario y se guarda permanentemente. Hemos


usado el método save() de la entidad en lugar del método save($entity)
del almacén.

También existe un método create() en la clase Entity, que funcionaría


exactamente igual (\Drupal\Core\Entity::create()).

- delete(array $entities). Elimina permanentemente las entidades


referenciadas en el array $entities.

Crear y actualizar usuarios

Ya hemos visto que podemos crear un usuario con el método create() del almacén,
y guardarlo con el método save(), del objeto usuario.

Cuando son otros módulos los que crean los usuarios, podemos intervenir durante
la creación implementando el hook:

- hook_ENTITY_TYPE_create($entity)

donde ENTITY_TYPE será sustituido por 'user'. Por ejemplo, para nuestro módulo
de ejemplo implementaríamos la función forcontu_users_user_create():

function forcontu_users_user_create(\Drupal\Core\Entity\EntityInterface $entity) {


\Drupal::logger('forcontu_users')->info('User created: @label',
['@label' => $entity->label()]);
}

[Link]
n/hook_ENTITY_TYPE_create/8

También podemos realizar acciones en estos otros puntos:

- hook_ENTITY_TYPE_presave($entity). Actúa antes de que la entidad


sea creada o actualizada.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 47


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

[Link]
n/hook_ENTITY_TYPE_presave/8

- hook_ENTITY_TYPE_insert($entity). Actúa cuando se crea una nueva


entidad. Se ejecuta una vez que la nueva entidad ha sido almacenada. En
este punto, por tanto, no podemos modificar la entidad.

[Link]
n/hook_ENTITY_TYPE_insert/8

- hook_ENTITY_TYPE_update($entity). Actúa cuando se actualiza una


entidad existente. Se ejecuta una vez que la entidad se ha almacenado.
En este punto, por tanto, no podemos modificar la entidad.

[Link]
n/hook_ENTITY_TYPE_update/8

Carga de usuarios

Durante la carga de entidades de usuario podemos utilizar el siguiente hook:

- hook_ENTITY_TYPE_load($entities). El parámetro pasado es un array


con las entidades cargadas, lo que nos permitirá realizar operaciones
sobre un conjunto de entidades, generalmente cargado con el método
loadMultiple().

[Link]
n/hook_ENTITY_TYPE_load/8

Eliminar usuarios

Cuando se elimina un usuario, podemos utilizar estos hooks:

- hook_ENTITY_TYPE_predelete($entity). Actúa antes de que se


elimine la entidad.

[Link]
on/hook_ENTITY_TYPE_predelete/8

- hook_ENTITY_TYPE_delete($entity). Actúa después de que la entidad


haya sido eliminada. Se utiliza generalmente para eliminar información
relacionada con la entidad que hayamos podido almacenar en otras tablas
o variables de configuración desde nuestro módulo.

[Link]
on/hook_ENTITY_TYPE_delete/8

48 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

Hooks del módulo User 26.5

El módulo User añade los siguientes hooks específicos para trabajar con usuarios:

[Link]

- hook_user_cancel($edit, $account, $method). Permite actuar antes de


que una cuenta de usuario sea cancelada.

[Link]
ser_cancel/8

Las acciones a realizar pueden depender del método de cancelación


indicado. Consulta la función user_cancel_methods() para más
información sobre los métodos de cancelación.

[Link]
ancel_methods/8

- hook_user_cancel_methods_alter(&$methods). Permite modificar


los métodos de cancelación de cuenta. Con este hook, un módulo puede
añadir nuevos métodos de cancelación, o modificar los existentes.

[Link]
ser_cancel_methods_alter/8

- hook_user_format_name_alter(&$name, $account). Permite


modificar el nombre de usuario que se muestra en el sitio. Puede utilizarse
un nombre alternativo para garantizar la privacidad del usuario. Este es
el valor que se obtiene al usar el método getDisplayName() del usuario.

[Link]
ser_format_name_alter/8

- hook_user_login($account). Actúa cuando el usuario se acaba de


loguear en el sitio. Permite realizar operaciones cuando el usuario accede
al sitio: mostrar un mensaje, redirigirlo a una página específica, registrar
el acceso, etc.

[Link]
n/8

- hook_user_logout($account). Actúa cuando el usuario cierra sesión en


el sitio (logout). Puede ser útil, por ejemplo, para registrar el cierre de
sesión.

[Link]
ser_logout/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 49


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

26.6 Información adicional de usuario

La clase UserData permite, a través del servicio [Link], añadir información


adicional relacionada con cada usuario. Esta información se almacena en la tabla
users_data.

Servicio: [Link]
Clase: Drupal\user\UserData

[Link]
Data/8

Los métodos de la clase son:

- get($module, $uid = NULL, $name = NULL). Obtiene el valor de la


variable $name. En función de los parámetros solicitados, se obtendrá un
resultado diferente:

o Con todos los parámetros, devuelve el valor concreto


almacenado para el usuario.
o Si solo se especifica $module y $uid, devuelve un array
asociativo con todos los datos almacenados por el módulo para
ese usuario.
o Si solo se especifica $module y $name, devuelve un array
asociativo con los valores de $name para cada usuario.
o Si solo se especifica $module, devuelve un array asociativo con
todos los valores para cada usuario.

[Link]
serData::get/8

- set($module, $uid, $name, $value). Almacena información adicional


relacionada con el usuario.

[Link]
serData::set/8

- delete($module = NULL, $uid = NULL, $name = NULL). Elimina la


información adicional relacionada con el usuario.

[Link]
serData::delete/8

Como ejemplo, vamos a analizar el módulo Contact del núcleo, que añade en el
formulario de edición del usuario el grupo de Opciones de contacto, desde
donde podemos indicar si el usuario activa el Formulario de contacto personal
[F26.6a].

50 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

F26.6a
Módulo Contact
Información adicional
añadida al usuario a través
del formulario de edición
de usuario.

El módulo implementa hook_form_FORM_ID_alter() para modificar el


formulario de edición de usuario (Form ID: user_form).

<?php

// File: /core/modules/contact/[Link]

/**
* Implements hook_form_FORM_ID_alter().
*
* Add the enable personal contact form to an individual user's account page.
*
* @see \Drupal\user\ProfileForm::form()
*/
function contact_form_user_form_alter(&$form, FormStateInterface $form_state) {
$form['contact'] = array(
'#type' => 'details',
'#title' => t('Contact settings'),
'#open' => TRUE,
'#weight' => 5,
);
$account = $form_state->getFormObject()->getEntity();
if (!\Drupal::currentUser()->isAnonymous() && $account->id()) {
$account_data = \Drupal::service('[Link]')->get('contact', $account->id(),
'enabled');
}
$form['contact']['contact'] = array(
'#type' => 'checkbox',
'#title' => t('Personal contact form'),
'#default_value' => isset($account_data) ? $account_data :
\Drupal::config('[Link]')->get('user_default_enabled'),
'#description' => t('Allow other users to contact you via a personal contact
form which keeps your email address hidden. Note that some privileged users such as
site administrators are still able to contact you even if you choose to disable this
feature.'),
);
$form['actions']['submit']['#submit'][] = 'contact_user_profile_form_submit';
}

Haciendo uso del método get() del servicio [Link], se obtiene el valor de la
variable 'enabled' almacenado para el usuario que se está editando.

$account_data = \Drupal::service('[Link]')->get('contact', $account->id(), 'enabled');

En la función de submit añadida, se utilizar el método set() para almacenar el valor


seleccionado en el formulario.

<?php

/**
* Submit callback for the user profile form to save the contact page setting.
*/
function contact_user_profile_form_submit($form, FormStateInterface $form_state) {
$account = $form_state->getFormObject()->getEntity();
if ($account->id() && $form_state->hasValue('contact')) {
\Drupal::service('[Link]')->set('contact', $account->id(), 'enabled', (int)
$form_state->getValue('contact'));
}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 51


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

Otras clases y servicios relacionados con


26.7
usuarios

Creación de roles

La clase Role se encarga de gestionar el tipo de entidad Rol.

[Link]
e/8

Para crear un rol de forma programada, podemos utilizar el método create() común
a todas las entidades. En el siguiente ejemplo creamos el rol Client (con ID client).

$role = \Drupal\user\Entity\Role::create(['id' => 'client',


'label' => 'Client']);
$role->save();

Otros métodos disponibles son:

- getPermissions(). Devuelve un array con los permisos asignados al rol.

- grantPermission($permission). Asigna el permiso $permission al rol.

- revokePermission($permission). Elimina el permiso $permission al rol.

- hasPermission($permission). Comprueba si el rol tiene el permiso


$permission.

Cambio de usuario

La clase AccountSwitcher, a través del servicio account_switcher, permite cambiar


de usuario. Se utiliza principalmente en funciones de test, para realizar acciones
con diferentes usuarios de prueba.

Servicio: account_switcher
Clase: AccountSwitcher
[Link]
p/class/AccountSwitcher/8

Los métodos principales son:

- switchTo($account). Cambia a otra cuenta de usuario.


- switchBack(). Vuelve a la cuenta de usuario anterior.

52 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 26. Usuarios y permisos

Hazlo desde la consola 26.8

En el apartado 14.5 vimos varios comandos de administración de usuarios desde


consola. Estos son otros comandos disponibles:

drupal generate:permissions

Genera permisos para el módulo (archivo .[Link]).

$ drupal user:role add userdesa1 gestor

[OK] Username userdesa1 () was added role gestor sucessfully

[Link]
[Link]

$ drupal generate:permissions
Enter the module name [admin_toolbar]:
> forcontu_console

Enter a permission [access content]:


> custom permission

Enter a title for the permission [Access content]:


> Custom permission

Enter a description for the permission [Allow access to my


content]:
> Allow users to access this module

Restrict Access [none]:


>

Do you want to add another permission (yes/no) [yes]:


> no

You can use this permission in the routing file like this:
forcontu_console.route:
requirements:
_permission: custom permission:

Generated or updated files

1 -
modules/custom/forcontu_console/forcontu_console.[Link]
desa1@[Link]:~/public_html/be/be2 $

El comando generará el archivo .[Link] en el directorio raíz de módulo:

custom permission:
title: 'Custom permission'
description: 'Allow users to access this module'

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 53


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Theming I: Creación de temas y


27
plantillas
Los temas permiten cambiar el aspecto gráfico o diseño del sitio. Un
Comparativa D8/D7
tema se compone de un conjunto de archivos con extensión
.[Link], que son archivos de plantilla en formato Twig para ser Theming
usados en diferentes áreas del sitio. Además de los archivos de
plantilla, tanto módulos como temas pueden implementar un La estructura de plantillas y organización
conjunto de funciones PHP que permiten intervenir en el sistema y del tema es muy parecida a la de Drupal 7,
modificar la salida final HTML. pero teniendo en cuenta que ahora se
utiliza Twig como motor de plantillas.
Es posible modificar un tema de forma sencilla cambiando las hojas
de estilo CSS y los archivos de plantilla. Cada plantilla acepta un Otro cambio importante es que en Drupal 8
conjunto predeterminado de variables, que serán sustituidas por sus ya no se usan funciones de temas, así que
valores reales al mostrar el sitio web. solo tendremos que trabajar con plantillas.

En esta Unidad veremos cómo crear un tema desde un tema base,


y cómo trabajar con las plantillas y funciones de preprocesamiento.
También veremos cómo ampliar las opciones de configuración del
tema, modificando su formulario de configuración.

Ten en cuenta que en este curso estudiamos el Theming desde el


punto de vista del desarrollador Back-End. En el curso de Front-End
Development ampliaremos estos contenidos y profundizaremos más
en la maquetación con HTML y CSS, y en el desarrollo de temas
responsive.

27
Contenidos de la Unidad
27.1 Creación de temas
27.2 Archivos de plantilla
27.3 Twig en Drupal
27.4 Funciones de preprocesamiento de plantillas
27.5 Formulario de configuración del tema
27.6 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 57


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

27.1 Creación de temas

Estructura de un tema

Un tema se compone de un conjunto de archivos que definen la capa de


presentación del sitio.

Los temas contribuidos o desarrollados (custom), se almacenan en la carpeta


/themes de nuestra instalación. Dentro de la carpeta de cada tema, veremos una
estructura de archivos y carpetas similar a esta [F27.1a]:

>themes/
F27.1a >exampletheme/
Estructura de un tema [Link]
Estructura de archivos y [Link]
[Link]
carpetas típica de un tema.
[Link]
[Link]
[Link]

>config/
>install/
[Link]
>schema/
[Link]

>css/
[Link]

>js/
[Link]

>images/
[Link]

>templates/
[Link]
[Link]
[Link]

Comentamos a continuación para qué sirven los distintos archivos del tema:

- *.[Link]. De los archivos que contiene el tema, solo uno de ellos es


obligatorio, el archivo .[Link]. En este archivo se incluye la definición
del tema y algunas opciones de configuración general (regiones, archivos
CSS, etc.). El nombre del archivo *.[Link] define el nombre de
sistema del tema, y suele estar contenido en una carpeta del mismo
nombre.

Por ejemplo, en la carpeta exampletheme encontraremos el archivo


[Link]. El nombre interno del tema será
exampletheme. No es obligatorio que el nombre de la carpeta se
corresponda con el nombre interno del tema, pero sí recomendable.

- *.[Link]. En este archivo se definen las librerías que utiliza el


tema (archivos CSS y JS).

58 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

- *.[Link]. Los breakpoints o puntos de ruptura definen en un


diseño responsive las condiciones para que el tema cambie en función del
dispositivo desde el que se está visualizando la página.

- *.theme. Es un archivo PHP que contiene código asociado al tema,


generalmente de preprocesamiento de la salida.

- [Link] (o [Link]). Es el archivo por defecto incluido en el tema.


Podemos cambiar este logo por el nuestro o subir una nueva imagen con
el logo desde la configuración del tema.

- [Link]. Es una imagen con la vista previa del tema, que se


utiliza en el listado de temas (Apariencia).

- css/. Carpeta donde se ubican los archivos CSS del tema (*.css). Estos
archivos se definen en el archivo *.[Link].

- js/. Carpeta donde se ubican los archivos JavaScript del tema (*.js). Estos
archivos se definen en el archivo *.[Link].

- images/. Imágenes utilizadas por el tema (iconos, fondos, bordes, etc.).

- templates/. Incluye los archivos de plantilla, que tienen la extensión


*.[Link]. En Drupal 8 los archivos de plantilla están escritos en Twig,
que es una mezcla de HTML, variables y código Twig para obtener la
salida HTML final. Veremos más sobre este formato en los próximos
apartados.

Las plantillas incluidas en el tema sobrescriben la salida predeterminada.


Por ejemplo, el archivo [Link] modifica la salida para todos los
nodos del sitio. Al incluir este archivo en el tema, estamos sobrescribiendo
el archivo de plantilla por defecto, incluido en el módulo node del núcleo.
Si no incluimos una plantilla determinada, el sistema utilizará la plantilla
por defecto.

- config/. En esta carpeta se incluyen archivos de configuración (*.yml)


con parámetros por defecto que se tendrán en cuenta durante la
instalación del tema.

El archivo .[Link]

El archivo [Link] incluye la definición del tema y algunas opciones


de configuración general (regiones, archivos CSS, etc.).

El nombre del archivo [Link] define el nombre de sistema del


tema, y suele estar contenido en una carpeta del mismo nombre. Al igual que los
nombres de módulos, el nombre de sistema debe escribirse en minúsculas y sin
espacios, usando guiones bajos en su lugar.

Una vez definido el archivo .[Link], el tema estará visible en el listado de Temas
del sitio:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 59


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

URL Apariencia
Administración  Apariencia
/admin/appearance

El archivo .[Link] contiene una serie de parámetros (clave/valor) en formato


YAML. Todos los parámetros disponibles se pueden encontrar en:

[Link]
infoyml-file

Veamos un ejemplo con algunos de los parámetros más utilizados [F27.1b]:

name: 'Foo Theme'


F27.1b type: theme
Archivo [Link] base theme: bartik
Parámetros típicos en el description: 'A flexible, recolorable theme with many regions and
archivo de definición de un a responsive, mobile-first layout.'
tema. package: Examples
core: 8.x

libraries:
- foo_theme/global-styling

regions:
header: Header
primary_menu: 'Primary menu'
secondary_menu: 'Secondary menu'
page_top: 'Page top'
page_bottom: 'Page bottom'
highlighted: Highlighted
featured_top: 'Featured top'
breadcrumb: Breadcrumb
content: Content
sidebar_first: 'Sidebar first'
sidebar_second: 'Sidebar second'
featured_bottom_first: 'Featured bottom first'
featured_bottom_second: 'Featured bottom second'
featured_bottom_third: 'Featured bottom third'
footer_first: 'Footer first'
footer_second: 'Footer second'
footer_third: 'Footer third'
footer_fourth: 'Footer fourth'
footer_fifth: 'Footer fifth'

- name. Es el nombre del tema, tal y como se muestra en el listado de


administración. Si se compone de más de una palabra, se escribirá entre
comillas simples.

- type. En temas siempre tendrá el valor theme.

- base theme. Si nuestro tema hereda de otro, incluiremos aquí el nombre


del tema base. Si no se especifica, se usará por defecto el tema base
'stable'.

- description. La descripción del tema que se muestra en el listado de


temas (Apariencia).

- package. Nombre del grupo para agrupar temas relacionados.

- core. Especifica la versión del núcleo con la que es compatible. En general


le daremos el valor 8.x.

60 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

- libraries. La forma recomendada para añadir archivos CSS y Javascript


es incluyendo librerías. Los archivos incluidos en la librería se cargarán
siempre que se utilice el tema. Veremos más adelante cómo definirlas.

- regions. Las regiones son áreas del tema donde se podrán ubicar los
bloques del sitio. Se incluirá un listado de regiones indicando, para cada
una de ellas, el nombre de sistema y el nombre visible en el área de
administración de bloques. Cuando el nombre de la región tiene varias
palabras, se incluye entre comillas simples.

La región 'content', que muestra el contenido de la página, es la única


región obligatoria.

Regiones

Por compatibilidad entre módulos y plantillas del núcleo, se recomienda utilizar los
mismos nombres de regiones que utilizan los temas del núcleo. De esta forma,
cuando cambiamos el tema del sitio, los bloques se posicionarán correctamente en
la región correspondiente del nuevo tema.

El contenido de cada región es dinámico, y dependerá de los bloques que se


añadan a cada una de ellas desde el área de administración de bloques del sitio.

Las regiones se utilizan en la plantilla [Link], con el formato


page.nombre_region [F27.1c]:

<div id="page-wrapper">
F27.1c
<div id="page">
<header id="header" class="header" role="banner" aria- Regiones
label="{{ 'Site header'|t }}"> Presentación de regiones
<div class="section layout-container clearfix"> desde la plantilla
{{ page.secondary_menu }} [Link].
{{ [Link] }}
{{ page.primary_menu }}
</div>
</header>
{% if [Link] %}
<div class="highlighted">
<aside class="layout-container section clearfix"
role="complementary">
{{ [Link] }}
</aside>
</div>

El sistema se encargará de crear este contenido buscando los bloques activos, el


orden de los mismos, etc. y devolverá el HTML ya compuesto para cada región.
Como veremos, este código HTML depende de otras plantillas secundarias que
también definiremos en nuestro tema.
Para añadir regiones personalizadas, seguiremos estos pasos:

1. Definir la región en el archivo .[Link] del tema.


2. Añadir el HTML adicional y la variable page.nombre_region en la
plantilla [Link] del tema.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 61


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Temas base y subtemas

A la hora de incorporar un tema a nuestro sitio, podemos seguir varias estrategias:

1. Instalar un tema normal. Como tema "normal" entendemos cualquier


tema orientado a ser utilizado directamente en nuestro sitio (son normales
todos los temas excepto los temas base).

Hay que tener cuidado con las actualizaciones. El sistema nos avisará
de posibles actualizaciones del tema, pero si lo actualizamos, podemos
sobrescribir los cambios realizados en el tema (por ejemplo, en plantillas
o archivos CSS).

2. Instalar un tema normal cambiando el nombre. Para evitar el


problema de las actualizaciones, una opción es cambiar el nombre del
tema, generalmente por el nombre de nuestro sitio. El procedimiento es
muy sencillo, descargamos el tema y cambiamos tanto los nombres de
archivos como los nombres de funciones donde se haga referencia al
nombre original. Una vez modificado, podemos subir el tema al
alojamiento e instalarlo de la forma habitual.

También podemos usar esta estrategia para reutilizar un tema del núcleo
sin que se vea afectado por las actualizaciones de Drupal.

3. Crear un subtema a partir de un tema base. Los temas base están


diseñados para ser extendidos a través de subtemas. Aportan
funcionalidad y estilos básicos, que serán heredados por los subtemas
implementados. Todas las modificaciones en las plantillas y hojas de estilo
se realizarán exclusivamente en los subtemas, manteniendo intactos los
archivos del tema base. El tema base podrá actualizarse a nuevas
versiones, ya que los cambios los estamos realizando únicamente en el
subtema.

Existen muchos temas base, disponibles en el reporitorio de temas de


Drupal junto al resto de temas. Algunos de estos temas base son:

- Bootstrap ([Link]
- Basic ([Link]
- AdaptiveTheme ([Link]
- Omega ([Link]
- Otros temas base clásicos como Zen ([Link]
o Fusion ([Link] aún no tienen una
versión para Drupal 8.

Los temas base pueden tener instrucciones de instalación diferentes, por


lo que lo primero que tenemos que hacer es localizar estas instrucciones
en la página del proyecto o en el archivo [Link] incluido en la
carpeta del tema.

4. Crear un subtema a partir de un tema normal. Cualquier tema puede


ser extendido a través de un subtema. De esta forma el subtema hereda
todos los estilos y plantillas del tema original. Dado que los cambios los
realizaremos en nuestro subtema, el tema original que estamos usando

62 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

de base puede actualizarse "sin problemas". Cuando se pueda prever que


las actualizaciones de un tema pueden afectar a nuestro tema,
optaremos, en su lugar, por la estrategia 2.

5. Crear un tema personalizado desde cero. Esta opción está


prácticamente descartada debido al coste que implica. Si queremos
empezar casi desde cero, lo mejor será partir de un tema base (punto 3)
o, como mínimo, usar como tema base el tema 'classy' (o 'stable', que es
el tema base por defecto). De esta forma, nuestro tema heredará los
posibles cambios que se produzcan en el núcleo a nivel de theming.

6. Instalar un tema con perfil de instalación. Cada vez es más común


encontrar temas, especialmente de pago, que se instalan como un perfil
de instalación o distribución de Drupal. Estas distribuciones, además del
tema, crean todos los elementos necesarios (tipos de contenido, vistas,
taxonomías, etc.) y contenidos de ejemplo. De esta forma, una vez
instalado, tendremos un sitio DEMO muy completo sobre el empezar a
realizar modificaciones.

Es una forma muy rápida de empezar a trabajar y tener un sitio listo en


poco tiempo, pero con algunos inconvenientes:

o Instalar una distribución implica instalar Drupal desde cero, por


lo que no podríamos aplicarlo sobre un sitio ya instalado.
o Los perfiles de instalación añaden elementos (tipos de contenido,
bloques, vistas, etc.), por lo que tenemos que realizar un estudio
previo del sitio para entender cómo se ha montado y poder así
modificar cualquier elemento.
o Las estrategias seguidas por el autor del tema/distribución a la
hora de implementar la estructura del sitio, nos fuerzan a seguir
caminos que no siempre son los más adecuados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 63


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Creación de un subtema a partir de Bartik

Como ya comentamos, es posible crear un subtema a partir de cualquier tema,


aunque no se trate de un tema base. En este caso práctico vamos a crear un tema
llamado Bartik Forcontu (b_forcontu) usando como tema base el tema Bartik
del núcleo.

Bartik es a su vez un subtema de Classy, que es un tema base añadido al núcleo


en Drupal 8. También podemos crear un subtema a partir de Classy directamente,
aunque en este caso práctico lo haremos a partir de Bartik.

Paso 1. Crear la carpeta y el archivo de definición del tema

Comenzamos trabajando en local y más adelante subiremos la estructura de


carpetas y archivos creada al servidor.

a) Crea la carpeta b_forcontu. El tema irá ubicado en la carpeta /themes.

Nota: Si es necesario, adapta la configuración de tu IDE para que pueda


acceder y sincronizar también esta carpeta, o crea un proyecto nuevo.

b) Crea el archivo b_forcontu.[Link] en la carpeta raíz del tema. Dentro


de este archivo añadiremos el siguiente contenido [F27.1d]:

name: 'Bartik Forcontu'


F27.1d
type: theme
Creación de un base theme: bartik
subtema description: 'Subtema a partir de Bartik'
Archivo de información del core: '8.x'
tema Bartik Forcontu.
libraries:
- b_forcontu/global-styling

Los parámetros especificados son:

- name. Nombre del tema tal y como se ve en el listado de temas. El


nombre de sistema viene definido por el nombre del archivo .[Link],
así que en nuestro caso el nombre de sistema es b_forcontu.

- type. El tipo será theme.

- base theme. Se indica el nombre de sistema del tema que se utilizará


como base, bartik en nuestro caso.

- core. Versión del núcleo (8.x)

- libraries. Creamos un grupo de librerías llamado global-styling. En el


siguiente paso definiremos los archivos CSS que se incluyen dentro de
este grupo.

64 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Paso 2. Crear el archivo de definición de librerías

A continuación, crearemos, también en la carpeta raíz, el archivo


b_forcontu.[Link] [F27.1e].

global-styling: F27.1e
css:
theme: Creación de un
css/[Link]: {} subtema
Definición de archivo de
estilos CSS.

Estamos indicando que el tema tendrá un nuevo archivo [Link] ubicado en la


carpeta css.

Paso 3. Crear el archivo CSS

a) Crea la carpeta css (dentro de b_forcontu).


b) Entra en la carpeta css y crea el archivo [Link]. Añade, por ahora, el
siguiente contenido al archivo [F27.1f]:

#header { F27.1f
background-color: #C7D0D6; Creación de un
background-image: none; subtema
}
Ejemplo de estilos CSS.

Paso 4. Archivos de logo y vista previa

Sube a la carpeta raíz los archivos correspondientes al logo por defecto ([Link])
e imagen de vista previa del tema ([Link]). Puedes crear tus propias
imágenes o copiarlas desde el tema bartik (/core/themes/bartik).

Paso 5. Instalar el tema Bartik Forcontu

Sube la carpeta del tema al servidor, dentro de /themes. La estructura que


tendremos en el servidor será la siguiente [F27.1g]:

>themes/ F27.1g
>b_forcontu/
b_forcontu.[Link] Creación de un
b_forcontu.[Link] subtema
[Link] Estructura de carpetas y
[Link] archivos del tema.
>css/
[Link]

Desde Apariencia, verás el nuevo tema en la lista de Temas desinstalados.


Selecciona Instalar y seleccionar por defecto.

Si la instalación se ha realizado correctamente, accediendo a la página principal del


sitio verás que se ha aplicado un tema similar a Bartik, pero con el color de fondo
de la cabecera modificado [F27.1h].

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 65


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

F27.1h
Creación de un
subtema
Presentación de la página
con el nuevo tema.

Tipos de modificaciones del tema

Las modificaciones en el diseño del tema se pueden realizar a dos niveles:

- Modificando los archivos de plantilla (.[Link]). Podemos


modificar las plantillas para:

o Cambiar el orden de los elementos existentes, tanto estáticos


(HTML) como dinámicos (variables).

o Incluir nuevos elementos, tanto estáticos (fragmentos de HTML


y CSS) como dinámicos (variables permitidas, como veremos en
próximos cursos).

o Eliminar u ocultar elementos de la plantilla. Podemos eliminar o


comentar parte del código, tanto estático como dinámico, para
que no se muestre al aplicar la plantilla.

- Modificando los archivos de hojas de estilos (.css). Podemos


modificar las hojas de estilo para:

o Modificar estilos existentes en las plantillas.

o Crear nuevos estilos, en cuyo caso tendrán que ser referenciados


correctamente en el código HTML de la plantilla.

o Eliminar estilos que no se van a usar.

66 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Modificaciones del tema y caché

Cuando realicemos modificaciones en los archivos del tema, para que se apliquen
los cambios realizados, puede que necesitemos limpiar tanto la caché del
navegador (Control + F5), como la caché del sitio, desde:

- Área de administración:
Administración  Configuración  Desarrollo  Rendimiento
[Vaciar todas las cachés]

- Opciones de Desarrollo incluidas en Devel (desde Admin Toolbar o Web


Profiler).

- Desde consola, con Drush o Drupal Console.

Durante el desarrollo o modificación de un tema, conviene activar esta opción


aportada por el módulo Devel (Opciones de desarrollo) [F27.1i]:

- Reconstruir el registro de temas con cada carga de página.

De esta forma, evitamos tener que vaciar la caché del sitio constantemente.

F27.1i
Caché del sitio
Opción para reconstruir la
caché del tema en cada
carga de página.

Para la modificación de archivos CSS, tenemos que desactivar la caché de archivos


CSS, desde Rendimiento (desactivar la opción Combinar archivos CSS).

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 67


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Añadir archivos CSS y JS

Es posible modificar el aspecto de un tema a través de las hojas de estilo CSS. No


se requieren conocimientos de programación, pero sí conocimientos de
maquetación web con CSS. Será en el curso de Front-End donde estudiaremos
HTML y CSS en profundidad.

Como hemos comentado, los archivos CSS se ubican en la carpeta /css del tema,
y se declaran en el archivo theme_name.[Link]. Para modificar un estilo
en concreto, podemos modificar los archivos existentes o declarar y crear un nuevo
archivo CSS, que registraremos dentro de la librería.

En la Figura [F27.1j] se muestra un ejemplo de archivo .[Link], donde se


referencia al archivo css/[Link] y al archivo js/[Link]. Si el archivo Javascript
contiene jQuery, es necesario añadir la dependencia con la librería core/jquery,
ya que Drupal 8 solo carga jQuery cuando es necesario.

F27.1j foo:
version: 1.x
Librerías css:
Definición de librería para theme:
incluir archivos CSS y JS. css/[Link]: {}
js:
js/[Link]: {}
dependencies:
- core/jquery

En los temas se añade generalmente una librería con nombre global-styling, que
cargará todos los archivos CSS en todas las páginas donde se use el tema
[F27.1k]:

F27.1k global-styling:
version: 1.x
Librerías
css:
Librería global-styling theme:
típica utilizada en los css/[Link]: {}
temas. css/[Link]: {}
css/[Link]: {}
css/[Link]: { media: print }

Es importante tener en cuenta que los archivos CSS se cargarán en el orden


especificado.
En el siguiente enlace encontrás más información sobre carga de librerías asociadas
a un tema:

[Link]
javascript-js-to-a-drupal-8-theme

68 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Modificación de estilos con CSS

Para modificar un estilo necesitamos conocer la estructura HTML (etiquetas,


atributos y clases) que nos permite llegar hasta un elemento concreto. En los
navegadores modernos disponemos de una herramienta para desarrolladores, el
Inspector de elementos, que nos permite ver sobre la página, el contenido
HTML y las clases CSS que se aplican sobre ese elemento.

En la Figura [F27.1l] podemos ver cómo funciona el inspector de elementos


en Chrome (botón derecho sobre cualquier elemento de la página, opción
Inspeccionar):

- A. Página actual. Se resalta el elemento seleccionado.


- B. HTML. Se resalta el código HTML que genera el elemento seleccionado
en A.
- C. Estilos CSS que se están aplicando a ese elemento.

F27.1l
Inspector de elementos
Inspector de elementos de
Chrome para ver el código
HTML y los estilos CSS
aplicados.

Como ejemplo, si queremos cambiar el color del título de la página y tenemos este
código HTML [F27.1m]:

<div class="site-branding__text">
<div class="site-branding__name"> F27.1m
<a href="/" title="Inicio" rel="home">Experto en Drupal 8</a> Ejemplo de código
</div> HTML
</div>

Podemos cambiar el color del texto aplicando el siguiente estilo CSS [F27.1n]:

.site-branding__text a{
F27.1n
color: #000;
} Ejemplo de estilos CSS

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 69


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

.site-branding__name a{
color: #000;
}

Pero, ¿dónde añadimos este código?

En primer lugar, para poder localizar el archivo CSS correcto, tenemos que
desactivar la caché de archivos CSS, desde Rendimiento (desactivar la opción
Combinar archivos CSS).

Una vez desactivada esta opción, si inspeccionamos el código y miramos en la


sección C [F27.1k], veremos que el atributo color se está aplicando en un estilo
del archivo /core/themes/bartik/css/[Link]. En el siguiente código se
resalta el estilo que está afectando realmente al título [F27.1o].

.region-header,
F27.1o .region-header a,
Ejemplo de estilos CSS .region-header li [Link]-active,
.site-branding__text,
.site-branding,
.site-branding__text a,
.site-branding a,
.region-secondary-menu .menu-item a,
.region-secondary-menu .menu-item [Link]-active {
color: #fffeff;
}

Tenemos localizado el archivo y el código donde se aplica el estilo que queremos


cambiar, pero, ¿podemos modificar directamente este archivo para
cambiar el código de color? La respuesta es NO.

Nuestro tema es un subtema de Bartik, que es un tema del núcleo, y el archivo


[Link] es un archivo incluido en Bartik. Si lo modificamos directamente,
estaríamos modificando un archivo del núcleo, algo que no se debe hacer bajo
ninguna circunstancia. Entre otras cosas, porque cuando actualicemos Drupal,
perderemos el cambio realizado.

Así que el código lo tenemos que incluir en un archivo de nuestro tema,


concretamente en el archivo /themes/b_forcontu/css/[Link] que creamos
previamente.

El código que contendrá el archivo, teniendo en cuenta el código que ya contenía


anteriormente, será [F27.1p]:

#header {
F27.1p background-color: #C7D0D6;
Ejemplo de estilos CSS background-image: none;
}

.site-branding__text a{
color: #000;
}

Por último, actualiza la página para ver los cambios (título en negro) [F27.1q]. Si
no se actualiza, prueba lo siguiente, en este orden:

1. Actualizar la caché de Drupal.

70 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

2. Actualizar la caché del navegador (Control + F5).

F27.1q
Estilos CSS
Resultado final.

Utilizando el Inspector de elementos del navegador, podemos observar el nuevo


código CSS que se está aplicando al título. El código anterior sigue mostrándose, pero
tachado, ya que el código del tema Bartik Forcontu, al evaluarse en último lugar,
prevalece sobre los estilos aportados por el tema base Bartik [F27.1r].

F27.1r
Estilos CSS
Usamos el inspector de
elementos para verificar
los nuevos estilos
aplicados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 71


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

27.2 Archivos de plantilla

Qué son las plantillas

Para entender cómo funcionan los temas, es necesario entender primero cómo
funcionan las plantillas o templates.

Una plantilla es un archivo de texto con extensión .[Link], donde se mezcla


código estático o fijo en HTML, con otros fragmentos de programación (en Twig),
que incluyen variables o expresiones. Cuando Drupal carga el sitio web, se
sustituirán las variables y expresiones por sus correspondientes valores, que junto
con el contenido estático dará lugar al código HTML final que se mostrará en el
navegador.

Como ejemplo, revisemos el siguiente código de una plantilla:

<h1>{{ title }}</h1>

El código Twig empieza con {{ y termina con }}. En este código se imprime por
pantalla el contenido de la variable title. Esta variable se utiliza para indicar el título
de la página, un valor dinámico que depende de la página que se esté cargando
en cada momento.

Ahora bien, supongamos que hemos creado dos páginas en nuestro sitio, con los
títulos "Aviso legal" y "Quiénes somos". Al aplicar la plantilla, el resultado final será
distinto para cada una de las páginas, en concreto:

Página "Aviso legal":

<h1>Aviso legal</h1>

Página "Quiénes somos":

<h1>Quiénes somos</h1>

El código finalmente generado es un código estático con marcas HTML, pudiendo


incluir también CSS (y Javascript). Este es el código que se envía al navegador del
usuario para representar el sitio web y la página particular que se está cargando.
Los navegadores incluyen una opción "Ver código fuente" que permite ver el código
HTML generado.

72 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Twig vs PHPtemplate

Twig es el motor de plantillas utilizado en Drupal 8. En Drupal 7 se utilizaba


PHPtemplate, un motor de plantillas basado en PHP. Podemos resumir las
ventajas de Twig frente a PHPtemplate en:

- Mayor seguridad. Al no incluir código PHP, no existe la posibilidad de


añadir código directamente en la plantilla, lo que evita que se cometan
errores y posibles agujeros de seguridad.

- Limpieza del código. Al no permitirnos meter código PHP en las


plantillas, el código tendrá que añadirse obligatoriamente donde
corresponde, en el módulo que hayamos desarrollado. De este modo toda
la lógica de negocio queda en los módulos y no se deriva hacia las
plantillas.

- Facilidad de uso y extensibilidad. Como veremos en los cursos de


Back-End Development y Front-End Development, es muy sencillo crear y
extender plantillas en formato Twig.

Estructura de plantillas de un tema

Una vez entendido el concepto de plantilla, vamos a analizar más a fondo cada uno
de los archivos de plantilla que componen un tema de Drupal: [Link],
[Link], [Link], [Link] y [Link],
entre otros.

Drupal tiene sus propios archivos de plantillas por defecto, que utiliza en el caso
de que el tema activado en el sitio no disponga de ellos. Es decir, si el tema no
incluye un archivo [Link], a la hora de mostrar bloques Drupal utilizará
el archivo [Link] incluido por defecto en el núcleo (este archivo en
particular lo aporta el módulo Block del núcleo, y está disponible en
/core/modules/block/templates/[Link]).

Por tanto, si el tema con el que estamos trabajando no incluye un fichero


[Link] pero queremos variar la manera de la que se muestran los
bloques, debemos copiar este archivo por defecto en la carpeta templates del
tema, y a continuación trabajar con él. Drupal, al encontrar un archivo
[Link] en el tema, utilizará éste archivo en lugar del archivo del núcleo.

Cada archivo de plantilla aporta el diseño de un elemento. En la Figura [F27.4a]


se muestran coloreados los archivos de plantilla principales y cómo afectan a cada
elemento del sitio.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 73


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

F27.2a
Plantillas de un tema
Cada archivo de plantilla
se encarga de la capa de
presentación de un
elemento del sitio.
- [Link] se
encarga de configurar la
página HTML y de
presentar el contenido de
la plantilla [Link],
que a su vez recibe
contenidos de otras
plantillas interiores, como
[Link], que
genera la salida de
presentación de los
bloques.

Trabajando de forma
anidada se obtiene la
presentación final de la
página que se está
cargando en cada
momento.

Imagen obtenida de:


[Link]
(adaptada a Drupal 8) A continuación, describiremos cada uno de estos archivos y veremos algunas de
las variables típicas que se pueden utilizar. Como ejemplo tomaremos las plantillas
incluidas en el núcleo de Drupal.

La plantilla [Link]

Este es el archivo de plantilla de más alto nivel, y es el encargado de definir la


estructura de la página HTML. Será el encargado de mostrar las etiquetas <html>,
<head> y <body>, entre otras [F27.2b].

Si el tema activo (o el tema base) no incluye este archivo, Drupal utilizará la plantilla
por defecto, ubicada en:

/core/modules/system/templates/[Link].

F27.2b <!DOCTYPE html>


[Link] <html{{ html_attributes }}>
<head>
Plantilla incluida en el <head-placeholder token="{{ placeholder_token|raw }}">
módulo system del núcleo. <title>{{ head_title|safe_join(' | ') }}</title>
<css-placeholder token="{{ placeholder_token|raw }}">
<js-placeholder token="{{ placeholder_token|raw }}">
</head>
<body{{ attributes }}>
<a href="#main-content" class="visually-hidden focusable">
{{ 'Skip to main content'|t }}
</a>
{{ page_top }}
{{ page }}
{{ page_bottom }}
<js-bottom-placeholder token="{{ placeholder_token|raw }}">
</body>
</html>

74 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Algunas de las variables que se pueden utilizar en esta plantilla son:

- head_title. Título de la página para utilizar en la etiqueta <title>.

- logged_in. Indica si el usuario está logueado en el sitio.

- node_type. Si la página es un nodo, indicará el tipo de contenido.

- page. Contenido de la página en HTML, ya procesado y listo para mostrar


en el navegador. Este contenido proviene de la plantilla [Link].

Todas las variables disponibles, se describen en la cabecera de la plantilla:

{#
F27.2c
/**
* @file [Link]
* Default theme implementation for the basic structure of a single Drupal page.
*
Cabecera con las variables
* Variables: disponibles en la plantilla.
* - logged_in: A flag indicating if user is logged in.
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
* May contain one or more of the following:
* - title: The title of the page.
* - name: The name of the site.
* - slogan: The slogan of the site.
* - page_top: Initial rendered markup. This should be printed before 'page'.
* - page: The rendered page markup.
* - page_bottom: Closing rendered markup. This variable should be printed after
* 'page'.
* - db_offline: A flag indicating if the database is offline.
* - placeholder_token: The token for generating head, css, js and js-bottom
* placeholders.
*
* @see template_preprocess_html()
*
* @ingroup themeable
*/
#}

La plantilla [Link]

Esta plantilla genera todo el código contenido en la página, y una vez procesada
se envía a la plantilla [Link] a través de la variable {{ page }}. Algunas
de las variables que se pueden utilizar son [F27.2d]:

- base_path. Ruta de la instalación de Drupal. Si la instalación se ha


realizado en el directorio raíz del dominio, devuelve "/".

- is_front. Indica si la página actual es la portada del sitio.

- logged_in. Indica si el usuario está logueado en el sitio.

- is_admin. Indica si el usuario es administrador del sitio.

- front_page. Enlace a la página de inicio del sitio.

- $title. Título de la página.

- messages. Mensajes de error o notificaciones.


- node. El objeto node, si se está cargando una página en la que se

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 75


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

muestra un nodo.

- Regiones. Variables que imprimen el contenido de cada región:

o [Link]
o page.primary_menu
o page.secondary_menu
o [Link]
o [Link]
o [Link]
o page.sidebar_first
o page.sidebar_second
o [Link]
o [Link]

Si el tema activado no incluye este archivo, Drupal utiliza el que tiene por defecto,
ubicado en /core/modules/system/templates/[Link].

F27.2d <div class="layout-container">


[Link]
<header role="banner">
Plantilla incluida en el {{ [Link] }}
módulo system del núcleo. </header>

{{ page.primary_menu }}
{{ page.secondary_menu }}

{{ [Link] }}

{{ [Link] }}

{{ [Link] }}

<main role="main">
<a id="main-content" tabindex="-1"></a>{# link is in
[Link] #}

<div class="layout-content">
{{ [Link] }}
</div>{# /.layout-content #}

{% if page.sidebar_first %}
<aside class="layout-sidebar-first" role="complementary">
{{ page.sidebar_first }}
</aside>
{% endif %}

{% if page.sidebar_second %}
<aside class="layout-sidebar-second" role="complementary">
{{ page.sidebar_second }}
</aside>
{% endif %}

</main>

{% if [Link] %}
<footer role="contentinfo">
{{ [Link] }}
</footer>
{% endif %}

</div>

76 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

La plantilla [Link]

Esta plantilla genera la presentación del nodo. El resultado de la plantilla se muestra


en la plantilla de página ([Link]) dentro de la variable de región
{{ [Link] }} [F27.2e].

Si el tema activado no incluye este archivo, Drupal utiliza el que tiene por defecto,
ubicado en /core/modules/node/templates/[Link].

<article{{ attributes }}>


F27.2e
{{ title_prefix }} [Link]
{% if not page %} Plantilla incluida en el
<h2{{ title_attributes }}> módulo node del núcleo.
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endif %}
{{ title_suffix }}

{% if display_submitted %}
<footer>
{{ author_picture }}
<div{{ author_attributes }}>
{% trans %}Submitted by {{ author_name }} on {{ date }}{%
endtrans %}
{{ metadata }}
</div>
</footer>
{% endif %}

<div{{ content_attributes }}>


{{ content }}
</div>

</article>

A continuación se muestran algunas variables que se pueden utilizar en esta


plantilla:

- node. El objeto node.

- label. El título del nodo.

- content. El contenido del nodo (o un fragmento o adelanto del nodo si


se trata de un listado de nodos). Es un array que contiene todos los
elementos del nodo. Se puede imprimir el contenido completo usando la
variable {{ content }}, o imprimir un elemento en particular {{
content.field_example }}.

- author_picture. La imagen de usuario del autor del nodo.

- date. Fecha de creación del nodo.

- author_name. El nombre de usuario del autor del nodo.

- url. La URL del nodo.

- display_submitted. Indica si se debe publicar la información de envío


(autor y fecha del envío).

- attributes, title_attributes, content_attributes, author_attributes.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 77


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Atributos HTML.

- view_mode. Modo de presentación. Por ejemplo, contenido completo


(full) o resumen (teaser).

- teaser. Indica si el nodo se está mostrando en modo Resumen.

- page. Indica si el nodo se está mostrando en modo Contenido completo


(full).

- logged_in. Indica si el usuario está logueado en el sitio.

- is_admin. Indica si el usuario es administrador del sitio.

La plantilla [Link]

Se utiliza para generar la presentación de los bloques de una página. Si el tema


activado no incluye este archivo, Drupal utiliza el que tiene por defecto, ubicado
en /core/modules/block/templates/[Link].

Algunas de las variables disponibles en esta plantilla son [F27.2f]:

- plugin_id. Id interno de la implementación del bloque.

- label. El título del bloque.

- content. El contenido del bloque.

- attributes. Atributos HTML asociados al bloque. Por ejemplo, id o class.

- title_attributes. Atributos HTML asociados al título del bloque.

- title_prefix y title_suffix. Prefijo y sufijo del título.

F27.2f <div{{ attributes }}>


[Link] {{ title_prefix }}
{% if label %}
Plantilla incluida en el <h2{{ title_attributes }}>{{ label }}</h2>
módulo block del núcleo. {% endif %}
{{ title_suffix }}
{% block content %}
{{ content }}
{% endblock %}
</div>

78 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Otros archivos de plantilla

Existen muchas más plantillas que se encargan de otras áreas del sitio. Otros
ejemplos de archivos de plantilla son:

- [Link]. Genera la presentación de cualquiera de las regiones


del tema. El archivo por defecto de esta plantilla se encuentra en
/core/modules/system/templates/[Link].

- [Link]. Muestra la salida para los comentarios del sitio. El


archivo por defecto está en:
/core/modules/comment/templates/[Link].

- [Link]. Muestra la salida de la página de


mantenimiento del sitio. El archivo por defecto de esta plantilla se
encuentra en /core/modules/system/templates/maintenance-
[Link].

Por último, los módulos también pueden tener plantillas propias. Por ejemplo, el
módulo Forum incorpora varias plantillas que sirven para configurar cómo se
visualizan los mensajes del foro ([Link], [Link], etc.).

Cada módulo puede incluir varios archivos de plantillas, que utilizará desde su
propio código para mostrar las nuevas herramientas que añade al sitio. Si
queremos modificar cualquier de estas plantillas nunca debemos hacerlo
directamente en el archivo del módulo. Primero copiaremos el archivo en la carpeta
templates del tema, y ese será el archivo de plantilla que modificaremos, sin
cambiar su nombre original.

Depuración de plantillas

Una de las mayores complicaciones durante el desarrollo, es localizar qué plantilla


es la encargada de generar una determinada salida HTML. Para facilitar esta tarea,
Twig incluye una opción de depuración, que activaremos únicamente durante el
desarrollo del sitio:

1) Localiza el archivo sites/default/[Link]. Si el archivo no existe,


copia el archivo [Link] (en la misma carpeta), y renómbralo
como [Link].

Ten en cuenta que, tanto la carpeta default como el archivo [Link]


están protegidos contra escritura, así que tendrás que cambiar los
permisos para poder copiar y editar el archivo [Link]. Puedes
cambiar los permisos a 777, realizar los cambios, y dejar que el sistema
recupere los permisos originales.

2) Dentro de la sección [Link], cambiar el valor de debug a true.

parameters:
[Link]:
debug: true

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 79


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

3) Vacía la caché del sitio.

4) Accede al Informe de estado. Este paso recupera los permisos


originales de la carpeta default.

5) No olvides desactivar esta opción en el entorno de producción.

La información de depuración se mostrará como comentario HTML, en el código


fuente de la página generada. Por ejemplo, en Chrome, podemos usar las opciones
Ver código fuente de página o Inspeccionar.

Cada plantilla comienza con una etiqueta <!-- THEME DEBUG -->, e incluye la
siguiente información de depuración:

- THEME HOOK. Hace referencia a la plantilla base que se utiliza. Por


ejemplo, si el valor es 'field', la plantilla base será [Link].

- FILE NAME SUGGESTIONS. Más adelante veremos qué son las


sugerencias de plantillas (o sugerencias de nombres de plantilla). Una
misma plantilla puede tener diferentes nombres, de forma que actúa de
una forma más específica. Por ejemplo:

o [Link]. Es genérica, actúa para todas las páginas.


o [Link]. Es más específica, actúa cuando la
página es la portada (front) del sitio.

Cuando existen las dos plantillas, se aplica la plantilla más específica. De


hecho, el listado ya viene ordenado por orden de prioridad.

En el listado de nombres de plantilla sugeridos, la plantilla que se está


actualizando en la página actual viene señalada con una x.

- BEGIN/END OUTPUT. Marca el inicio y el fin de la salida HTML devuelta


por la plantilla. Tanto en el inicio como en el fin, se indica la ruta completa
del archivo de plantilla utilizado. Ten en cuenta que las plantillas pueden
contener otras plantillas.

Ejemplo 1: Inicio del archivo, con referencia a la plantilla html, que es la plantilla
de mayor nivel. Esta plantilla se cierra justo al final del archivo.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'html' -->
<!-- FILE NAME SUGGESTIONS:
* [Link]
* [Link]
x [Link]
-->
<!-- BEGIN OUTPUT from
'core/themes/classy/templates/layout/[Link]' -->
<!DOCTYPE html>
<html lang="es" ...>
<head>
...
</body>
</html>
<!-- END OUTPUT from 'core/themes/classy/templates/layout/[Link]' -->

80 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Ejemplo 2: Plantilla que se aplica al título del contenido. En este caso se está
usando la plantilla [Link], que es una plantilla específica
para los títulos de los nodos.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
* [Link]
x [Link]
* [Link]
* [Link]
* [Link]
* [Link]
-->
<!-- BEGIN OUTPUT from 'core/themes/classy/templates/field/field--
[Link]' -->
<span property="schema:name" data-quickedit-field-
id="node/9/title/es/teaser" class="field field--name-title field--
type-string field--label-hidden">Adipiscing Nimis Proprius</span>

<!-- END OUTPUT from 'core/themes/classy/templates/field/field--


[Link]' -->

Si queremos modificar la plantilla que presenta el título de los nodos (Ejemplo 2),
tenemos que ver primero dónde se encuentra. En este ejemplo, la plantilla está
localizada en el tema classy del núcleo, así que no podemos modificarla
directamente. Lo que haremos será copiar esa plantilla a la carpeta templates de
nuestro tema.

Una vez que la plantilla está en nuestro tema, recarga la página y comprueba que
la ruta del archivo es la esperada. Vacía caché si no ves el cambio reflejado. El
resto de información mostrada es exactamente igual, ya que el nombre de plantilla
sigue siendo el mismo, pero ubicada en nuestro tema. Ahora ya podemos modificar
la plantilla.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
* [Link]
x [Link]
* [Link]
* [Link]
* [Link]
* [Link]
-->
<!-- BEGIN OUTPUT from 'themes/b_forcontu/templates/field--node--
[Link]' -->
<span property="schema:name" data-quickedit-field-
id="node/9/title/es/teaser" class="field field--name-title field--
type-string field--label-hidden">Adipiscing Nimis Proprius</span>

<!-- END OUTPUT from 'themes/b_forcontu/templates/field--node--


[Link]' -->

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 81


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

La función dump()

Al activar la depuración en Twig, también tenemos disponible la función dump()


que podemos utilizar dentro una plantilla.

- {{ dump(variable) }}. Muestra el valor de la variable especificada.


- {{ dump() }}. Muestra todas las variables disponibles en la plantilla,
con sus correspondientes valores.

Si has instalado el módulo Devel Kint (incluido en Devel), podrás usar la función
kint(), que mejora la presentación del resultado.

- {{ kint(variable) }}. Muestra el valor de la variable especificada.


- {{ kint() }}. Muestra todas las variables disponibles en la plantilla, con
sus correspondientes valores.

Tanto en PHP como en Twig, se recomienda utilizar Kint.

F27.2g
Depuración en Twig
Valores de variables
mostrados con Kint.

82 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Web Profiler

El icono Tema de Web Profiler muestra información del tema cargado, como el
tiempo de renderizado, el número de llamadas a plantillas, bloques cargados, etc.
[F27.2h]

F27.2h
Web Profiler
Icono Tema.

Haciendo clic en el icono accedemos a información ampliada: Regiones, listado


completo de plantillas que se han utilizado en la carga de la página, etc. [F27.2i]

F27.2i
Web Profiler
Información de Tema
ampliada.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 83


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Sugerencias de plantillas (Template suggestions)

Ya hemos comentado que, para modificar una plantilla, primero tenemos que
copiarla a nuestro tema. En muchas ocasiones no nos interesa realizar cambios
para todos los casos en que se usa una plantilla, sino solo para casos específicos.

Por ejemplo, podríamos necesitar modificar la plantilla de bloque ([Link])


solo para el bloque de búsqueda (bloque del módulo search con nombre de sistema
form-block). En estos casos podemos hacer una copia de la plantilla base
[Link] y cambiar el nombre para hacerlo específico: block--search-
[Link].

No es nada complicado si localizas la salida del bloque en los comentarios de


depuración. En este caso vemos que bartik ya implementa una plantilla específica
para el bloque de búsqueda, localizada en:

- core/themes/bartik/templates/[Link]

Para modificarla, copiaremos esta plantilla a nuestro tema, sin necesidad de


cambiar el nombre del archivo.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* [Link]
x [Link]
* [Link]
* [Link]
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block--
[Link]' -->
<div class="search-block-form contextual-region block block-search
container-inline" data-drupal-selector="search-block-form"
id="block-b-forcontu-formulariodebusqueda" role="search">

<h2>Formulario de búsqueda</h2>

Veamos algunos ejemplos más:

- [Link]. Afecta únicamente a la plantilla page cuando se


está cargando el nodo con nid 1.

- [Link]. Plantilla page cuando la página cargada es un


nodo.

- [Link]. Afecta al bloque delta (nombre interno)


definido en el módulo module. Como vimos anteriormente, la plantilla
[Link] sigue este patrón.

- [Link]. Afecta a todos los nodos de un tipo determinado.


Por ejemplo, [Link] afectaría a todos los nodos de tipo
artículo.

Puedes consultar otras combinaciones posibles en:


[Link]

84 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Sugerencias de plantillas personalizadas

A través de hook_theme_suggestions_alter() podemos añadir nuevas


sugerencias de nombres de plantillas, que actuarán según unas condiciones
específicas.

hook_theme_suggestions_alter(array &$suggestions, array $variables, $hook)

[Link]
tion/hook_theme_suggestions_alter/8

Como alternativa, si vamos a actuar sobre un HOOK específico, podemos utilizar la


siguiente función:

hook_theme_suggestions_HOOK_alter(array &$suggestions, array $variables)


[Link]
tion/hook_theme_suggestions_HOOK_alter/8

Este hook se puede implementar tanto en módulos como en temas. Para


implementarlo en un tema, crearemos primero el archivo nombre_tema.theme,
en la carpeta raíz. El archivo .theme es un archivo PHP donde podemos añadir
funcionalidad programada al tema.

Como ejemplo, añadiremos un par de sugerencias de plantillas específicas para


nodos (HOOK = node), que serán alternativas a la plantilla [Link].

- [Link]. Plantilla del nodo de tipo type


cuando el usuario está autenticado.

- [Link]. Plantilla de cualquier nodo cuando el


usuario está autenticado.

<?php

/**
* @file
* Functions to support theming in the Bartik Forcontu theme.
*/

/**
* Implements hook_theme_suggestions_HOOK_alter() for HOOK 'node'.
*/
function b_forcontu_theme_suggestions_node_alter(array &$suggestions,
array $variables) {

if (\Drupal::currentUser()->isAuthenticated()) {
$suggestions[] = 'node__' . 'logged_in';

$node = $variables['elements']['#node'];
if($node->getType()){
$suggestions[] = 'node__' . $node->getType() . '__logged_in';
}
}
}

Las nuevas sugerencias se añaden a las existentes, y se utilizan de la misma forma,


copiando a nuestro tema la plantilla base para ese HOOK (o la que esté actuando

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 85


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

en ese momento), y cambiando el nombre del archivo por el nombre sugerido.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* [Link]
* [Link]
* [Link]
* [Link]
* [Link]
* [Link]
* [Link]
x [Link]
-->
<!-- BEGIN OUTPUT from
'core/themes/bartik/templates/[Link]' -->

Un par de cosas a tener en cuenta:

- El array $suggestions es pasado por referencia, así que podemos


modificar o eliminar las sugerencias existentes.

- Los elementos en el array $suggestions están ordenados de menor a


mayor especifidad, que es el orden inverso al mostrado en el código de
depuración [F27.2j]. Generalmente, será suficiente con añadir nuestras
sugerencias al final del array. Si añadimos varias sugerencias, tendremos
que hacerlo en el orden adecuado, de menor a mayor especificidad.

F27.2j
Sugerencias de
plantillas
Array $suggestions.

86 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Twig en Drupal 27.3

Como ya hemos comentado, en las plantillas el código Twig se mezcla entre código
HTML para generar el código HTML final que se envía al navegador. En el apartado
11.5 ya hicimos una breve introducción a Twig, que ahora adaptaremos a su uso
dentro de Drupal.

Para ampliar la información contenida en este apartado, se aconseja consultar los


siguientes enlaces:

- Documentación oficial de Twig:


[Link]

- Estándares de codificación de Twig:


[Link]

- Estándares de codificación de Twig en Drupal:


[Link]

Comentarios

Para escribir comentarios en el código, utilizaremos {# #}. Si el comentario tiene


una única línea, la apertura y cierre se realiza en la misma línea. Si el comentario
tiene varias líneas, los separaremos con un salto de línea. Siempre se debe intentar
que las líneas de comentarios tengan menos de 80 caracteres.

{# Comentario de una línea #}

{#
Comentario de varias líneas
nota: todo este código es un comentario y no se ejecutará.
tampoco las variables que podamos incluir dentro, como {{ title }}
#}

DocBlock

Un DocBlock es un bloque de documentación que se coloca al principio de un


archivo de plantilla. Se trata de un bloque de comentarios y, por tanto, estará
delimitado por {# y #}.

El archivo de plantilla se comenta con una directiva @file y una lista con las
variables disponibles (Available variables), enviadas por las funciones de
preprocesamento, a las que también se hace referencia a través de directivas
@see. Por ejemplo:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 87


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

{#
/**
* @file
* Default theme implementation to present a feed item in an aggregator page.
*
* Available variables:
* - url: URL to the originating feed item.
* - title: Title of the feed item.
* - content: All field items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {{ content|without('field_example') }} to temporarily suppress the printing
* of a given element.
* - attributes: HTML attributes for the wrapper.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
*
* @see template_preprocess_aggregator_item()
*
* @ingroup themeable
*/
#}
<article{{ attributes }}>
{{ title_prefix }}
<h3>
<a href="{{ url }}">{{ title }}</a>
</h3>
{{ title_suffix }}
{{ content }}
</article>

Para ver otros ejemplos reales de codificación, recomendamos el análisis directo


de los archivos del núcleo de Drupal y de otros módulos contribuidos.

La directiva @ingroup themeable indica que la plantilla está dentro del grupo
themeable. Esta directiva solo se incluye en las plantillas por defecto, así que no
deberíamos incluirla al sobrescribir una plantilla en nuestro tema.

Variables

Imprimir variables

Para imprimir el valor de una variable, esta se encierra entre llaves dobles
{{ variable }}. Veamos algunos ejemplos:

a) Ejemplo de variable simple:

{{ title }}

b) Variable con atributos (proveniente de un objeto en PHP):

{{ [Link] }}

c) Variable con elementos (proveniente de un array en PHP):

{{ foo['bar'] }}

d) Si el nombre del atributo contiene caracteres especiales (como por


ejemplo un guión – que podría ser interpretado como "menos"), podemos

88 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

utilizar la función attribute():

{{ attribute(foo, 'data-bar') }}

Sería equivalente a mostrar el valor de '[Link]-bar'.

Comprobación de variables

Esta es una expresión muy típica en Drupal. Comprobamos si la variable está


definida y tiene un valor antes de imprimirla. El código HTML que encapsula a la
variable, también se incluye dentro del if.

No es necesario usar el filtro 'is defined' para comprobar si la variable está


disponible.

{% if foo %}
<div>{{ foo }}</div>
{% endif %}

Asignar variables

Twig nos permite utilizar variables auxiliares dentro de la plantilla. Utilizaremos la


etiqueta set, entre {% y %} (en lugar de {{ }}).

{% set foo = 'bar' %}


{% set foo = [1, 2] %}

Estas variables auxiliares se pueden imprimir o utilizar en otras expresiones.

Variables globales

En todas las plantillas están disponibles estas variables globales:

- _self. Devuelve un objeto de tipo Twig_Template, con información de la


plantilla actual. Los métodos disponibles se pueden consultar en:

[Link]
p/class/Twig_Template/8

Recuerda que puedes utilizar las funciones de depuración dump() y kint()


para ver la estructura y contenidos de cualquier variable.

Si imprimimos la variable directamente, {{ _self }}, devuelve la ruta y


nombre del archivo de plantilla actual, desde el directorio raíz del sitio.
Por ejemplo:

themes/b_forcontu/templates/[Link]

- _context. Se trata de un array con información del contexto actual, como

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 89


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

todas las variables que están disponibles en la plantilla. Además de las


variables que llegan a la plantilla, también muestra las variables que se
han definido dentro de la plantilla.

{{ dump(context) }} es equivalente a {{ dump() }}. También con


kint().

- _charset. Juego de caracteres (por ejemplo, "UTF-8").

Filtros

Los filtros se utilizan para modificar las variables de alguna forma. Por ejemplo, un
filtro puede servir para pasar una cadena a mayúsculas (upper).

Para usar un filtro sobre una variable, primero se escribe la variable sobre la que
se aplicará el filtro, y luego el nombre del filtro separado por el símbolo pipe |. Se
pueden concatenar varios filtros, separados por |, que se aplicarán a la variable en
orden.

{% set foo = 'welcome' %}


{{ foo|upper }}

{# La salida será: WELCOME #}

{{ 'welcome'|upper }}

{# también se puede utilizar el filtro directamente sobre


una cadena, obteniéndose el mismo resultado
#}

Los filtros también se pueden aplicar a una sección de código, con la siguiente
estructura:

{% filter upper %}
Este texto se convertirá a mayúsculas
{% endfilter %}

Algunos de los filtros disponibles son:

- upper. Pasa a mayúsculas todas las letras de una cadena.

- lower. Pasa a minúsculas todas las letras de una cadena.

{{ 'WELCOME'|lower }}
{# Resultado: welcome #}

- capitalize. Pasa a mayúsculas sólo la primera letra de una cadena.

{{ 'welcome home'|capitalize }}

{# Resultado: Welcome home #}

90 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

- title. Pasa a mayúsculas la primera letra de cada palabra de la cadena.

{{ 'welcome home'|title }}
{# Resultado: Welcome Home #}

- date. Permite dar formato a una fecha. Funciona como la función date
de PHP.

{{ "now"|date("d/m/Y") }}
{# Resultado: 13/02/2016 #}

- join. Une los elementos de un array en una cadena. Por defecto se utiliza
una cadena vacía como separador (sin separación), pero se puede
especificar cualquier otro caracter.

{{ [1, 2, 3]|join }}
{# Resultado: 123 #}

{{ [1, 2, 3]|join(',') }}
{# Resultado: 1,2,3 #}

- sort. Ordena un array.

{% set foo = [3, 2, 4, 1]|sort %}


{# El contenido de la variable for será: [1, 2, 3, 4] #}

- length. Devuelve el número de elementos de un array o el número de


caracteres de una cadena.

{% set count = users|length %}


{# Almacena en la variable count el número de elementos de user #}

El listado completo de filtros de Twig se puede consultar en:

[Link]

Filtros específicos de Drupal

Además de los filtros que vienen de base con Twig, Drupal incorpora una serie de
filtros adicionales, que se pueden consultar en:

[Link]
templates

Estos filtros están declarados en TwigExtension::getFilters():

[Link]
p/function/TwigExtension::getFilters/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 91


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

- trans (o t). Se utiliza para traducir cadenas desde la plantilla. Al


igual que hacemos con todos los textos incluidos directamente en el
código de un módulo, los textos que se añaden en una plantilla también
deben pasar por la función t() de traducción.

Cuando se trate de una cadena simple, sin patrones de reemplazo,


usaremos el filtro |t.

<a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home"


class="site-logo"></a>

<h2>{{ 'Book traversal links for'|t }} {{ book_title }}</h2>

<b>{{ 'Not triggered'|t }}</b>

Cuando la cadena tenga variables o patrones de reemplazo, usaremos el


filtro {% trans %}:

{% trans %}
Submitted by {{ author_name }} on {{ date }}
{% endtrans %}

{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}

{% trans %}
This {{ [Link] }} has a length of: {{ count }}. It contains:
{{ [Link]|placeholder }} and {{ token.bad_text }}.
{% endtrans %}

- placeholder. Devuelve el texto enfatizado (<em></em>). También


realiza HTML escaping, convirtiendo ciertos caracteres especiales a sus
correspondencias en HTML.

{% set temp = 'The value is < 5' %}

{{ temp|placeholder }}

Imprime:

This value is < 5

El código HTML generado es:

<em class="placeholder">This value is &lt; 5</em>

92 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Estructuras de control

Estructuras de control: for

El for se puede utilizar como un foreach:

<h1>Users</h1>
<ul>
{% for user in users %}
<li>{{ [Link] }}</li>
{% endfor %}
</ul>

{# Imprime una lista HTML de nombres de usuario (atributo name)

<h1>Users</h1>
<ul>
<li>frangil</li>
<li>laurafornie</li>
<li>userfoo</li>
</ul>

#}

Si necesitamos utilizar tanto la clave como el valor, el equivalente al foreach sería:

{% for value, key in items %}


<div class="{{ key }}">{{ value }}</div>
{% endfor %}

Y también podemos usarlo como un for clásico, estableciendo un rango de


ejecución:

{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
{# Imprime 0, 1, 2, 3 #}

Estructuras de control: if

Permite evaluar una condición para realizar o no un conjunto de acciones.

{% if title|length > 0 %}
<h1>{{ title }}</h1>
{% endif %}
{# Imprime el valor de título (y las etiquetas h1) sólo si
la variable título no está vacía.
#}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 93


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Funciones

Twig dispone de un conjunto de funciones que podemos utilizar en las plantillas.

Por ejemplo, la función range, que ya utilizamos para establecer el rango de


valores de la estructura for anterior, devuelve un listado de números entre el
mínimo y el máximo indicados. También permite establecer el salto entre valores
(tercer parámetro).

{% for i in range(0, 6, 2) %}
{{ i }},
{% endfor %}
{# Imprime 0, 2, 4, 6 #}

La función max devuelve el valor máximo de entre los facilitados.

{{ max(1, 3, 2) }}
{# Imprime 3 #}

El conjunto completo de funciones disponibles por defecto se pueden consultar en:

[Link]

Funciones específicas de Drupal

Además de las funciones que vienen de base con Twig, Drupal incorpora una serie
de funciones adicionales, que se pueden consultar en:

[Link]

url() y path()

Dado un nombre de ruta y sus parámetros, ambas funciones generan la URL


correspondiente. La única diferencia es que url() genera la URL absoluta, y
path() genera la URL relativa. La ruta tiene que estar previamente definida en
un archivo .[Link].

Los parámetros requeridos por las funciones son:

url($name, $parameters, $options)


path($name, $parameters, $options)

Veamos algunos ejemplos:

a) URL absoluta a la página de inicio:

<a href="{{ url('<front>') }}">{{ 'Home'|t }}</a>

Genera el código HTML:

94 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

<a href="[Link]

b) URL relativa a la página de inicio:

<a href="{{ path('<front>') }}">{{ 'Home'|t }}</a>

Genera el código HTML:

<a href="/">Inicio</a>

c) URL relativa a la ruta forcontu_pages.simple (definida en el módulo


Forcontu Pages):

<a href="{{ path('forcontu_pages.simple') }}">{{ 'Forcontu Simple


Page'|t }}</a>

d) URL relativa a la página de perfil de usuario:

<a href="{{ path('[Link]', {'user': [Link]}) }}">{{


'View user profile'|t }}</a>

e) URL relativa a un nodo:

<a href="{{ path('[Link]', {'node': [Link]}) }}">{{


'View node page'|t }}</a>

link()

La función link() genera el código completo del enlace, pasándole como parámetros
el texto del enlace, la URL y los atributos adicionales que se añadirán a la etiqueta
<a>.

Es importante tener en cuenta que la URL no se corresponde con la cadena


obtenida por las funciones url() y path(), sino que debe ser un objeto de tipo
\Drupal\Core\Url.

{% for item in items %}


<li{{ [Link] }}>
{{ link([Link], [Link], {'class':['foo', 'bar']}) }}
</li>
{% endfor %}

attach_library($library)

Adjunta una librería a la plantilla.

{{ attach_library('classy/node') }}

Argumentos con nombre (named arguments)

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 95


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

En las llamadas a funciones, Twig permite que especifiquemos el nombre de cada


argumento, de la siguiente forma:

{% for i in range(low=1, high=10, step=2) %}


{{ i }},
{% endfor %}

Los nombres de los argumentos son los establecidos en la declaración de la función.

Las ventajas de los argumentos con nombre frente a los argumentos posicionales
son:

- Mejorar la legibilidad del código, al conocer el significado de cada


parámetro sin necesidad de recurrir a la API de la función.

- Saltarnos parámetros en la llamada a la función. Los parámetros no


establecidos usarán los valores por defecto.

- Reordenar los parámetros pasados a la función. Al estar nombrados, no


importará el orden en que se pasen a la función.

También podemos combinar argumentos posicionales y con nombre en la llamada


a una función. En ese caso, antepondremos siempre los argumentos posicionales,
en el orden esperado por la función.

96 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Herencia de plantillas: block, extends, parent()

Una de las opciones más potentes de Twig es la posibilidad de heredar desde otras
plantillas. En la plantilla "padre", o plantilla base, se pueden definir bloques
mediante la etiqueta block, y esos bloques serán los fragmentos que podrán
modificar las plantillas hijas.

Veamos un ejemplo. En primer lugar definimos una plantilla base con nombre de
archivo [Link] [F27.3a]. Esta plantilla servirá como primer nivel, e incluye
los elementos típicos de una página HTML (etiquetas <html>, <head> y <body>.

Dentro de esta plantilla se ha definido varios bloques:

- block head. Contenido del encabezado <head> del documento. Dentro


del encabezado "base" se ha añadido una hoja de estilos css/[Link].

- block title. Bloque que a su vez está dentro del bloque head. Sirve para
imprimir el atributo title de la página actual. Este bloque está vacío y serán
las plantillas hijas las que lo rellenen.

- block content. Bloque para el contenido. En la plantilla base este bloque


está completamente vacío y serán las plantillas hijas las que lo rellenen.

- block footer. Bloque para el pie de página, con el texto de copyright.

{# archivo [Link] #}

<!DOCTYPE html> F27.3a


<html> Herencia de plantillas
<head>
{% block head %} Plantilla base.
<link rel="stylesheet" href="css/[Link]" />
<title>{% block title %}{% endblock %} - Forcontu</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
&copy; Copyright 2017 by
<a href="[Link]
{% endblock %}
</div>
</body>
</html>

Ahora vamos a crear una plantilla que hereda de la plantilla anterior, completando
la información o añadiendo información adicional en los bloques [F27.3b].

{# archivo [Link] #}

{% extends "[Link]" %}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 97


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas


F27.3b
Herencia de plantillas
Plantilla hija, hereda de la
plantilla base. {% block title %}{{ title }}{% endblock %}
{% block head %}
{{ parent() }}
<link rel="stylesheet" href="css/[Link]" />
{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<div>{{ content }}</div>
{% endblock %}

- Al block title se le está pasando el valor de la variable {{ title }}

- En el block head primero llamamos a la función parent(), con lo que se


reutiliza todo el contenido de ese bloque en la plantilla base. Luego se
añade una referencia a otra hoja de estilos.

- Completamos el block content usando HTML y las variables title y


content. Por supuesto el sistema se tiene que encargar de que esas
variables lleguen a la plantilla.

- No hacemos referencia al block footer, así que se utilizará el bloque de


la plantilla base.

El HTML resultante una vez procesadas las plantillas con sus correspondientes
variables, tendría este aspecto [F27.3c]:

F27.3c <!DOCTYPE html>


<html>
Herencia de plantillas
<head>
Código HTML resultante. <link rel="stylesheet" href="css/[Link]" />
<title>Home - Forcontu</title>
<link rel="stylesheet" href="css/[Link]" />
</head>
<body>
<div id="content"><h1>Home</h1>
<div>Contenido de la página</div>
</div>
<div id="footer">
&copy; Copyright 2017 by
<a href="[Link]
</div>
</body>
</html>

98 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Expresiones

Por último, vamos a ver algunas de las expresiones, tipos de variables y operadores
que podemos utilizar en Twig:

Arrays

Los arrays se expresan entre corchetes [] y con sus elementos separados por
comas.

["foo", "bar", "barz"]

Los arrays asociativos se definen con llaves {}, y con elementos clave:valor.

Cuando las claves son cadenas, se pueden escribir entre comillas simples o sin
comillas:

{ 'foo': 'foo', 'bar': 'bar' }


{ foo: 'foo', bar: 'bar' }

Las claves también pueden ser numéricas:

{ 2: 'foo', 4: 'bar' }

Las claves también pueden ser expresiones, siempre entre paréntesis:

{ (1 + 1): 'foo', (a ~ 'b'): 'bar' }

Operaciones matemáticas

Además de los operadores habituales, +, -, /, % (resto) y *, también podemos


usar estos otros operadores:

- //. Divide dos números y devuelve el valor entero redondeado.


- **. Elevado a. {{ 2 ** 3 }} significa 2 elevado a 3, cuyo resultado es 8.

Operadores lógicos

Los operadores lógicos disponibles son and, or y not. Los paréntesis sirven para
agrupar expresiones (expr).

Operadores de comparación

Además de los operadores básicos: ==, !=, <, >, >= y <=, podemos usar los
siguientes:

- starts with, ends with. Comprueba si una cadena empieza o termina


por otra cadena.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 99


Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

{% if 'foo' starts with 'f' %}


{% endif %}

{% if 'foo' ends with 'o' %}


{% endif %}

Operador in

El operador in comprueba si un elemento está contenido en un array o en una


cadena. También se puede usar negado, not in, para comprobar que el elemento
no está en la lista. Cuando se cumple la condición, devuelve true.

{% if 1 in [1, 2, 3] %}

{% if 5 not in [1, 2, 3] %}

{% if 'cd' in 'abcde' %}

Operador is

El operador is comprueba si una variable cumple una expresión. Es conocido como


operador de test, siendo el test la expresión con la que se compara la variable.
También se puede utilizar como negación, is not.

{% if value is odd %}

{% if [Link] is constant('Post::PUBLISHED') %}

{% if [Link] is not constant('Post::PUBLISHED') %}

Operador ..

Equivale a la función range(), creando una secuencia entre un mínimo y un


máximo.

{{ 1..5 }}

{{ range(1, 5) }}

Combinando el operador .. con el filtro join, podemos obtener una cadena cuyos
valores están separados por el separador indicado:

{{ (1..5)|join(', ') }}

{# Salida: 1, 2, 3, 4, 5 #}

Operador ~

Convierte todos los operandos en cadenas y las concatena.

{% set name = 'Fran' %}


{{ "Hello, " ~ name ~ "!" }}
{# Hello, Fran! #}

100 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Operador ternario

El operador ternario (A ? B : C) funciona igual que en PHP. Se evalúa la expresión


A. Si el resultado es true, devuelve B, y si es false, devuelve C.

{{ foo ? 'yes' : 'no' }}

Otras variantes:

{{ foo ?: 'no' }} equivale a {{ foo ? foo : 'no' }}


{{ foo ? 'yes' }} equivale a {{ foo ? 'yes' : '' }}

Etiqueta spaceless

La etiqueta spaceless elimina espacios y saltos de línea entre etiquetas HTML,


respetando los espacios que puedan tener las cadenas.

{% spaceless %}
<div>
<strong>foo bar</strong>
</div>
{% endspaceless %}

{# Salida: <div><strong>foo bar</strong></div> #}

Atributos HTML

En Drupal, muchas plantillas reciben la variable {{ attributes }}, con atributos HTML
para incorporar en una etiqueta. Podemos imprimir todos los atributos de una vez,
con {{ attributes }}, o acceder a los elementos dentro de attributes de forma
individual {{ [Link] }}, {{ [Link] }}, etc.

<div id="{{ [Link] }}" class="{{ [Link] }}"{{


attributes }}>
{{ content }}
</div>

Nótese que, en el ejemplo, aunque hemos usado los atributos de forma individual,
al final de la etiqueta se añade nuevamente {{ attributes }}, para que se impriman
otros atributos que puedan provenir de cualquier módulo.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 101
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Funciones de preprocesamiento de
27.4
plantillas
Como ya sabemos, las plantillas reciben una serie de variables. Antes de que estas
variables lleguen definitivamente a la plantilla, hay una serie de funciones, llamadas
de preprocesamiento, que pueden interceptarlas y modificarlas.

El orden de ejecución de las funciones de preprocesamiento es el siguiente:

Núcleo:

1. template_preprocess(&$variables, $hook). Crea un conjunto de


variables por defecto que pueden ser utilizadas por todos los hooks.

2. template_preprocess_HOOK(&$variables). Esta función irá


generalmente en el módulo que registra la plantilla. Por ejemplo, el módulo
Comment añade la función template_preprocess_comment() para
definir las variables de la plantilla [Link].

[Link]
ule/function/template_preprocess_comment/8

Módulos:

3. MODULE_preprocess(&$variables, $hook). Es la implementación de


hook_preprocess() dentro de un módulo. Esta función será llamada para
todas las plantillas.

4. MODULE_preprocess_HOOK(&$variables). Es la implementación de
hook_preprocess_HOOK() dentro de un módulo. Esta función se utiliza
cuando un módulo desea modificar las variables de la plantilla HOOK de
otro módulo.

Motor de temas (phptemplate):

5. ENGINE_engine_preprocess(&$variables, $hook). Esta función sólo


debe ser implementada por el motor de temas.

6. ENGINE_engine_preprocess_HOOK(&$variables). Esta función sólo


debe ser implementada por el motor de temas.

Tema:

7. THEME_preprocess(&$variables, $hook). Es la implementación de


hook_preprocess() dentro de un tema. Se implementa en el
archivo .theme del tema.

8. THEME_preprocess_HOOK(&$variables). Es la implementación de
hook_preprocess_HOOK() dentro de un tema. Se implementa en el
archivo .theme del tema, y actúa sobre una plantilla en particular
(HOOK).

102 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Hay que tener en cuenta que varios módulos pueden implementar funciones de
preprocesamiento sobre el mismo HOOK. El orden de ejecución de las mismas
dependerá del orden en que se ejecuten los módulos.

Como las últimas funciones en actuar son las implementadas en el tema, este
tendrá siempre la última palabra en cuando al tratamiento de variables.

Las funciones que vamos a implementar son:

hook_preprocess(&$variables, $hook)

[Link]
tion/hook_preprocess/8

hook_preprocess_HOOK(&$variables)

[Link]
tion/hook_preprocess_HOOK/8

Nótese que el parámetro $variables se pasa a la función por referencia


&$variables, de forma que los cambios que realicemos en el interior de la función
estarán disponibles también en el exterior, y la variable podrá ser reutilizada por
otras funciones con los cambios realizados.

Ejemplo 1:

Vamos a implementar hook_preprocess_THEME para añadir una clase CSS


adicional a la plantilla de comentarios, [Link].

Implementaremos en el archivo b_forcontu.theme, la función


b_forcontu_preprocess_comment():

<?php

/**
* Implements hook_preprocess_HOOK() for [Link].
*/
function b_forcontu_preprocess_comment(&$variables) {

$variables['attributes']['class'][] = 'forcontu';
}

Para probar el funcionamiento, crea un comentario en algún nodo del sitio.


Comprueba, inspeccionando el código fuente generado, que se ha añadido la clase
forcontu.

<!-- BEGIN OUTPUT from


'core/themes/bartik/templates/[Link]' -->
<article role="article" data-quickedit-entity-id="comment/14"
data-comment-user-id="1" about="/comment/14"
typeof="schema:Comment" class="forcontu comment js-comment
clearfix">
<span class="hidden" data-comment-
timestamp="1487699815"></span>

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 103
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

En este caso no hemos tenido que tocar la plantilla [Link], ni añadirla


a nuestro tema, ya que hemos modificado una variable que ya se utiliza dentro de
la plantilla original.

Ejemplo 2:

El contenedor de servicios de Drupal incluye el servicio twig, gestionado por la clase


\Drupal\Core\Template\TwigEnvironment:

[Link]
.php/class/TwigEnvironment/8

El método isDebug() devuelve true si el modo de depuración de Twig se


encuentra activado, y false en caso contrario.

En este ejemplo vamos a añadir una nueva variable llamda 'twig_debug', que
estará disponible en todas las plantillas, y que indicará si el modo de depuración
se encuentra activo.

Implementamos en nuestro tema la función hook_preprocess(), y realizamos la


llamada al servicio twig.

<?php

/**
* Implements hook_preprocess().
*/
function b_forcontu_preprocess(&$variables, $hook) {

$variables['twig_debug'] = \Drupal::service('twig')->isDebug();

Ahora la nueva variable twig_debug estará disponible en todas las plantillas del
sistema.

Para probar su funcionamiento, modificaremos la plantilla [Link],


añadiendo un bloque de texto indicando que el modo de depuración está activado.
Como la plantilla [Link] que se está utilizando actualmente es la del
tema base Bartik, tendremos que copiarla primero a nuestro tema, sin renombrarla.
Una vez en nuestro tema, añadiremos el siguiente código para mostrar un texto
(traducible), cuando la variable twig_debug sea verdadera.

<div id="page-wrapper">
<div id="page">
<header id="header" class="header" role="banner" aria-
label="{{ 'Site header'|t }}">
<div class="section layout-container clearfix">
{{ page.secondary_menu }}
{{ [Link] }}
{{ page.primary_menu }}
</div>
</header>
{% if twig_debug %}
<div class="twig-debug">
{{ 'Twig Debug is enabled'|t }}
</div>

104 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

{% endif %}
{% if [Link] %}

...

También añadiremos un estilo para la clase twig-debug en el archivo [Link] del


tema.

.twig-debug {
background-color: #ffeb3b;
}

El resultado esperado, una vez traducida la cadena desde Traducción de la


interfaz de usuario, se muestra en la Figura [F27.4a].

F27.4a
twig_debug
Nueva variable twig_debug
utilizada desde la plantilla
[Link].

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 105
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

27.5 Formulario de configuración del tema

Cada tema puede tener opciones de configuración específicas, que estarán


disponibles en:

Administración  Apariencia  Tema [Opciones]

Para añadir nuevas opciones en el formulario de configuración del tema,


implementaremos la función hook_form_system_theme_settings_alter().
Esta función se puede añadir al archivo .theme, o a un nuevo archivo llamado
[Link].

[Link]
tion/hook_form_system_theme_settings_alter/8

hook_form_system_theme_settings_alter(&$form, &$form_state)

Esta función permite alterar el formulario de configuración del tema, por lo que
recibe los parámetros habituales en el tratamiento de formularios: $form y
$form_state. $form es un parámetro pasado por referencia, así que todos los
cambios que hagamos, añadiendo, modificando o eliminando elementos, se verán
reflejados en el formulario.

Volvemos a la implementación de Bartik Forcontu para añadir un campo de tipo


checkbox con el que especificaremos si se quiere mostrar o no el texto de
"Depuración de Twig" añadido en la plantilla [Link].

El sistema se encargará de almacenar el valor del campo en la configuración que


se crea automáticamente para el tema ('b_forcontu.settings'). La variable dentro
de la configuración tiene el mismo nombre que el elemento de formulario. En
nuestro ejemplo, 'show_twig_message' [F27.5a].

F27.5a <?php
Formulario de
/**
configuración del tema
* Implements hook_form_system_theme_settings_alter().
Función que permite */
modificar el formulario de function b_forcontu_form_system_theme_settings_alter(&$form,
configuración del tema. \Drupal\Core\Form\FormStateInterface $form_state) {

$form['b_forcontu'] = array(
'#type' => 'fieldset',
'#title' => t('Bartik Forcontu settings'),
);

$form['b_forcontu']['show_twig_message'] = array(
'#type' => 'checkbox',
'#title' => t('Display "Twig Debug" message'),
'#default_value' => theme_get_setting('show_twig_message'),
'#description' => t('Check this option if you want to display
a message when Twig Debug is enabled.'),
);
}

106 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Para obtener el valor de un parámetro de configuración del tema utilizamos la


función theme_get_setting(), indicando el nombre del campo.

[Link]
ting/8

El resultado se muestra en la Figura [F27.5b].

F27.5b
Opciones de
configuración
Nuevo campo en el
formulario de Opciones de
configuración del tema.

Para poder utilizar este valor en las plantillas, tenemos que añadirlo a $variables a
través de alguna función de preprocesamiento. En nuestro ejemplo, como solo
estamos mostrando el texto en la plantilla [Link], implementaremos la
función hook_preprocess_page(). Aquí también usaremos la función
theme_get_setting() para obtener el valor del parámetro de configuración
[F27.5c].

<?php F27.5c
theme_get_setting()
/**
* Implements hook_preprocess_HOOK() for [Link]. Esta función permite
*/ obtener el valor del un
function b_forcontu_preprocess_page(&$variables) { parámetro del tema.

$variables['show_twig_message'] = theme_get_setting('show_twig_message');
}

Por último, modificaremos la plantilla [Link] para que solo muestre el mensaje
si la opción está activada [F27.5d].

{% if twig_debug and show_twig_message %} F27.5d


<div class="twig-debug">
[Link]
{{ 'Twig Debug is enabled'|t }}
</div> Utilización de la nueva
{% endif %} variable en una plantilla.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 107
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Valores por defecto

También podemos especificar valores por defecto para las variables de


configuración, que se añadirán durante la instalación del tema. Para ello
definiremos un archivo de configuración config/install/theme_name.[Link].

Para nuestro tema, crearemos el archivo:

config/install/b_forcontu.[Link]

F27.5e features:
node_user_picture: 1
Configuración por
comment_user_picture: 1
defecto
comment_user_verification: 1
Archivo YAML con la favicon: 1
configuración que se logo:
aplicará al instalar el tema. use_default: 1
favicon:
use_default: 1
show_twig_message: 1

Hemos añadido el nuevo campo personalizado, show_twig_message, y otras


opciones de configuración, que pueden ser añadidas por los temas base o el núcleo.

La forma más rápida de averiguar cuáles son las variables que podemos añadir
dentro de este archivo de configuración, es consultando directamente la
configuración creada al instalar el tema. Como ya hemos visto, lo podemos hacer
desde consola o desde Config Editor de Devel. Recuerda que el archivo de
configuración para un tema tiene el nombre theme_name.settings (Por ejemplo,
b_forcontu.settings).

Por último, deberás desactivar, desinstalar y volver a instalar el tema para probar
si se aplican los valores por defecto indicados en el archivo de configuración.

108 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Hazlo desde la consola 27.6

Instalación/desinstalación de temas

drupal theme:install

Instala el tema indicado. Si el tema ya está subido al servidor, lo activará. Con la


opción --set-default, podemos establecer el nuevo tema como tema por defecto
del sitio.

drupal theme:install options


drupal ti

$ drupal theme:install b_forcontu --set-default


The "Bartik Forcontu" theme has been installed successfully as
default theme

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

[Link]
[Link]

drupal theme:uninstall

Desinstala el tema indicado. Ten en cuenta que el tema no es borrado de su


carpeta.

drupal theme:uninstall options


drupal tu

Si intentamos desinstalar el tema por defecto, el comando nos devolverá un error.

$ drupal theme:uninstall b_forcontu

[ERROR] Theme "Bartik Forcontu" is the default theme and cannot


be uninstalled.

Para poder realizar el cambio, primero estableceremos el nuevo tema por defecto.
Esto lo podemos hacer desde Drush o Drupal Console, teniendo en cuenta que se
trata de un cambio en la configuración (ver comandos en apartado 19.5).

$ drush config-set [Link] default bartik


Do you want to update default key in [Link] config? (y/n): y

Una vez establecido un nuevo tema por defecto, ya podemos desinstalar el tema:

$ drupal theme:uninstall b_forcontu


The "Bartik Forcontu" theme has been uninstalled successfully

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 109
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

[Link]
[Link]

drupal theme:download

Descarga el tema en la carpeta /themes, sin instalarlo.

drupal theme:download tema


drupal td

$ drupal theme:download mayo


Getting releases for theme "mayo"

Please select your favorite release:


[0 ] 8.x-1.3
[1 ] 8.x-1.2
> 0

Downloading theme "mayo" release "8.x-1.3"


[OK] Theme "mayo" version "8.x-1.3" was downloaded successfully
at "/home/username/public_html/be/be1/themes/mayo"

[Link]
[Link]

110 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Otros comandos de gestión de módulos

drupal theme:path

Devuelve la ruta donde está instalado el tema.

drupal theme:path tema

$ drupal theme:path b_forcontu


themes/b_forcontu

[Link]
[Link]

drupal module:debug

Devuelve un listado con los temas disponibles, indicando, para cada uno de ellos,
si están instalados o desinstalados, el nombre de sistema y la versión.

drupal theme:debug
drupal tde

$ drupal theme:debug
------------ ----------------- ------------- ---------
Id Name Status Version
------------ ----------------- ------------- ---------
stark Stark Uninstalled 8.2.5
stable Stable Installed 8.2.5
seven Seven Installed 8.2.5
bartik Bartik Installed 8.2.5
classy Classy Installed 8.2.5
b_forcontu Bartik Forcontu Installed
------------ ----------------- ------------- ---------

[Link]
[Link]

Si en la llamada al comando incluimos como argumento el nombre de un tema de


los disponibles en el sitio, nos mostrará también las regiones del tema.

$ drupal theme:debug b_forcontu


Bartik Forcontu
Status : Uninstalled
Version : Regions
---------------- ----------------
sidebar_first Left sidebar
sidebar_second Right sidebar
content Content
header Header
primary_menu Primary menu
secondary_menu Secondary menu
footer Footer
highlighted Highlighted
help Help
page_top Page top
page_bottom Page bottom
breadcrumb Breadcrumb
---------------- ----------------

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 111
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 27. Theming I: Creación de temas y plantillas

Creación de temas

Comandos relacionados con la creación del esqueleto (archivos) de un tema.

drupal generate:theme

Genera un tema.

drupal generate:theme options


drupal gt

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:theme

// Welcome to the Drupal theme generator


Enter the new theme name []:
> Console Theme

Enter the module machine name [console_theme]:


> console_theme

Enter the theme Path [/themes/custom]:


> /themes

Enter theme description [My Awesome theme]:


>

Enter package name [Other]:


> Forcontu

Enter Drupal Core version [8.x]:


>

Base theme (i.e. classy, stable) [false]:


> classy

Enter the global styling library name [global-styling]:


>

Do you want to generate the theme regions (yes/no) [yes]:


> yes

Enter region name [Content]:


>

Enter region machine name [content]:


>

Do you want to add another region (yes/no) [yes]:


> no

Do you want to generate the theme breakpoints (yes/no) [yes]:


> no

Do you confirm generation? (yes/no) [yes]:


> yes

Generated or updated files

1 - be/be2/themes/console_theme/console_theme.[Link]
2 - be/be2/themes/console_theme/console_theme.theme

[Link]
console/content/es/commands/[Link]

112 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

28 Theming II: Theming en módulos


El sistema de temas de Drupal se basa en plantillas. Como ya
Comparativa D8/D7
sabemos, las plantillas se componen de código HTML y una serie de
variables, en formato Twig, que se sustituyen por sus valores para Theming en módulos
generar la salida HTML final. Pero antes de llegar a las plantillas, es Los arrays y elementos renderizables ya se
en los módulos en donde se genera la salida de páginas, bloques y usaban en Drupal 7, pero se han
otros elementos. Los módulos no generan el HTML directamente, incorporado cambios importantes,
sino que deben devolver un array renderizable con la información especialmente en su definición.
necesaria para que el sistema pueda generar el HTML
correspondiente. La forma de definir plantillas en módulos es
muy similar a Drupal 7, usando
En esta unidad veremos cómo implementar arrays y elementos hook_theme para declarar las plantillas.
renderizables. También veremos cómo se definen plantillas en
módulos y cómo crear extensiones adicionales (funciones y filtros) La creación de extensiones de Twig solo se
para aplicar en plantillas Twig.
aplica a Drupal 8.

Contenidos de la Unidad

28
28.1 Arrays renderizables
28.2 Elementos renderizables
28.3 Definición de plantillas en módulos
28.4 Crear extensiones de Twig
28.5 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 115
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

28.1 Arrays renderizables

El sistema de temas de Drupal se basa en plantillas. Como ya sabemos, las plantillas


se componen de código HTML y una serie de variables, en formato Twig, que se
sustituyen por sus valores para generar la salida HTML final.

Pero antes de llegar a las plantillas, es en los módulos en donde se genera la salida
de páginas, bloques y otros elementos. Los módulos no generan el HTML
directamente, sino que deben devolver un array renderizable con la información
necesaria para que el sistema pueda generar el HTML correspondiente.

El servicio renderer, a través de la clase \Drupal\Core\Render\Renderer, se


encarga en última instancia de convertir cada array renderizable a su
correspondiente salida HTML.

Servicio: renderer
Clase:
[Link]
Renderer/8

El sistema de temas llamará a este servicio múltiples veces para ir renderizando


cada elemento e ir componiendo la salida HTML final que se enviará al navegador.

Ya hemos visto algunos ejemplos de arrays renderizables en unidades anteriores:

- Al devolver la salida de una página, a través de su método controlador.


- Al devolver la salida de un bloque, a través de su método build().
- Al generar un formulario, a través del método buildForm().

En esta unidad crearemos el módulo Forcontu Theming (forcontu_theming).


Definiremos una primera página con ruta forcontu_theming.render y URL
/forcontu/theming/render, y controlador ForcontuThemingController::render.

<?php

/**
* @file
* Contains \Drupal\forcontu_theming\Controller\ForcontuThemingController.
*/

namespace Drupal\forcontu_theming\Controller;

use Drupal\Core\Controller\ControllerBase;

class ForcontuThemingController extends ControllerBase {

public function render() {


//definición del array $build
return $build;
}

Un array renderizable puede tener varios elementos (array de arrays), que se

116 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

renderizarán en orden. Como ejemplo, dentro del método render() vamos a


construir un array $build con varios elementos renderizables.

El primer elemento es una salida #markup, que es una cadena HTML directa.
Aunque podríamos especificar una cadena HTML completa, recuerda que utilizamos
siempre la función t() para traducir los textos de la interfaz.

//Example 1: markup
$build['forcontu_theming_markup'] = [
'#markup' => '<p>' . $this->t('Lorem ipsum dolor sit amet,
consectetur adipiscing elit.') . '</p>',
];

El segundo elemento es de tipo 'table'. En este caso, al definir el tipo de elemento


('#type' => 'table'), podremos utilizar propiedades adicionales específicas para ese
elemento. La propiedad #header es un array con los valores de las columnas de la
tabla, y la propiedad #rows un array donde cada fila de datos se expresa también
como array.

//Example 2: table
$header = ['Column 1', 'Column 2', 'Column 3'];
$rows[] = ['A', 'B', 'C'];
$rows[] = ['D', 'E', 'F'];

$build['forcontu_theming_table'] = [
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
];

Otra forma de renderizar una salida es a través de la propiedad '#theme', donde


se establece la plantilla que se encargará de renderizar el array. La plantilla dispone
de una serie de propiedades que son las que podemos utilizar en el array. En el
ejemplo utilizamos el valor '#theme' => 'item_list', que se corresponde con la
plantilla [Link].

[Link]

//Example 3: list
$list = ['Item 1', 'Item 2', 'Item 3'];

$build['forcontu_theming_list'] = [
'#theme' => 'item_list',
'#title' => $this->t('List of items'),
'#list_type' => 'ol',
'#items' => $list,
];

Como ya hemos comentado, estos elementos se convierten en HTML, y son


representados en el navegador, tal y como se muestra en la Figura [F28.1a].

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 117
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

F28.1a
Arrays renderizables
Salida en el navegador una
vez el array es renderizado.

Inspeccionando el código de la página, podemos ver el HTML generado:

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>

<table class="responsive-enabled" data-striping="1">


<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>A</td>
<td>B</td>
<td>C</td>
</tr>
<tr class="even">
<td>D</td>
<td>E</td>
<td>F</td>
</tr>
</tbody>
</table>

<div class="item-list">
<h3>Lista de ítems</h3>
<ol>
<li>Item 1</li>

<li>Item 2</li>

<li>Item 3</li>
</ol>
</div>

Propiedades de los arrays renderizables

Los arrays renderizables se componen de dos tipos de elementos: propiedades


(properties) e hijos (children). Las propiedades comienzan con '#' y aportan
información sobre cómo debe convertirse el elemento. Los hijos son a su vez arrays
renderizables que describen elementos hijos y que deben renderizarse junto con el
elemento padre. Generalmente el código HTML de los elementos hijos irá contenido
dentro del elemento padre.

Como ya hemos visto, según el tipo de elemento, el array podrá contener

118 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

propiedades adicionales. Ya vimos algunos de los elementos disponibles y sus


propiedades cuando estudiamos los elementos de formulario, que son un
subconjunto de los elementos renderizables. Veamos ahora algunas de las
propiedades más utilizadas en los arrays renderizables:

- #type. Tipo de elemento. Cuando se especifica un valor de #type,


estarán disponibles las opciones específicas de ese elemento. ya
estudiamos los tipos de elementos de formulario, y en el próximo apartado
veremos otros tipos de elementos renderizables de uso genérico.

- #theme. Se especifica la plantilla que se encargará de generar la salida


HTML para el elemento. Como veremos más adelante, los módulos
pueden definir sus propias plantillas, implementando hook_theme(). Las
variables disponibles en una plantilla son, a su vez, propiedades que
pueden ser especificadas en el array renderizable (anteponiendo el
símbolo #).

- #markup. Indica que el array renderizable facilita la salida directa en HTML.


Solo debe usarse con elementos muy simples, como párrafos delimitados
por <p></p>. En otros casos se recomienda usar un tipo de elemento
(#type) o plantilla (#theme) específicos.

- #plain_text. Indica que el array devuelve un texto plano. El texto


indicado será revisado para eliminar etiquetas y valores no permitidos.
Este atributo tiene prioridad con respecto a #markup.

- #allowed_tags. Esta propiedad se utiliza para limitar las etiquetas HTML


posibles en una cadena añadida a través de #markup.

- #prefix/#suffix. Permite añadir texto o código HTML antes y después de


un elemento.

- #pre_render. Array de funciones que se ejecutarán antes de renderizar el


elemento. De esta forma es posible modificar la presentación del elemento
antes de su construcción.

- #post_render. Array de funciones que se ejecutarán después de renderizar


el elemento. Estas funciones actuarán, por tanto, sobre el HTML generado.

- #theme_wrappers. Cuando un elemento tiene elementos hijos,


primero se renderizan los hijos (según la configuración individual de cada
uno de ellos) y la salida HTML es almacenada en la propiedad #children.
En #theme_wrappers podemos indicar un nombre de función de tema
que actuará sobre el contenido en #children, pudiendo añadir HTML
adicional a modo de envoltorio sobre los elementos hijos.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 119
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

Servicio renderer

Generalmente será suficiente con definir arrays renderizables, y dejar que el


sistema los renderice en el momento que lo necesite. Sin embargo, también
podemos renderizar directamente un array renderizable utilizando el servicio
renderer.

El servicio renderer, a través de la clase \Drupal\Core\Render\Renderer, se


encarga en última instancia de convertir cada array renderizable a su
correspondiente salida HTML.

Servicio: renderer
Clase:
[Link]
Renderer/8

En el siguiente ejemplo inyectamos el servicio 'renderer' en la clase controladora.


Dentro de la clase se define un método getDescription() que devuelve un mensaje
ya renderizado:

<?php

namespace Drupal\example_module;

use Drupal\Core\Render\RendererInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class TestClass extends ControllerBase {

protected $renderer;

public function __construct(RendererInterface $renderer) {


$this->renderer = $renderer;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('renderer')
);
}

public function getDescription() {


$build = [];
$build['warning'] = [
'#markup' => $this->t('Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Pellentesque mattis est ac magna
porta venenatis sed ut libero.'),
];

return $this->renderer->render($build);
}
}

El método render() es el encargado de, dado un array renderizable, transformarlo


en la salida HTML correspondiente.

[Link]
on/Renderer::render/8

120 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

public Renderer::render(&$elements, $is_root_call = FALSE)

La función recibe como parámetro el array renderizable $elements, que puede


contener uno o más elementos renderizables, incluyendo elementos hijos.

Elementos hijos

En un array renderizable, cada elemento puede tener propiedades (empiezan con #) o


elementos hijos (sin #). En el siguietne ejemplo, el elemento 'container' tiene las
propiedades '#prefix', '#suffix' y '#markup', y el elemento hijo 'list'.

public function renderChildren() {

$list = ['Item 1', 'Item 2', 'Item 3'];

$build = [
'container' => [
'#prefix' => '<div id="container">',
'#suffix' => '</div>',
'#markup' => $this->t('This is a container div'),
'list' => [
'#theme' => 'item_list',
'#title' => $this->t('List of items'),
'#list_type' => 'ol',
'#items' => $list,
],
],
];
return $build;
}

Si vemos el resultado, el elemento hijo se representa dentro del elemento padre,


que en este caso solo actúa como contenedor, añadiendo una etiqueta <div>. En
la Figura [F28.1b] hemos añadido un borde a la etiqueta <div> para comprobar
que, efectivamente, el elemento list se imprime dentro del elemento contenedor.

F28.1b
Arrays renderizables
Ejemplo de array
renderizable con elementos
hijos.

Esto mismo se observa analizando el HTML generado:

<div id="container">This is a container div


<div class="item-list">
<h3>Lista de ítems</h3>
<ol>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ol>
</div>
</div>

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 121
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

Modificar arrays renderizables

Como ya hemos comentado, el uso de arrays renderizables nos permite modificar


los elementos de una forma estructurada antes de que se genere su presentación
HTML.

El lugar desde donde debemos modificar un elemento dependerá de dónde se esté


usando. Veamos algunos ejemplos:

- hook_form_alter(). Permite realizar modificaciones sobre cualquier


formulario antes de que sea renderizado. En este caso podemos modificar
el array renderizable $form.

- hook_block_view_alter(). Permite modificar el contenido de un


bloque, actuando sobre el array $build devuelto por el método build().

- hook_entity_view_alter(). Permite modificar la presentación de


cualquier entidad, actuando sobre el array renderizable $build.

Enlaces de interés

Más información sobre arrays renderizables:


[Link]
on/Renderer::render/8
[Link]

Introducción al sistema de temas


[Link]
p/themeable/8

Render API
[Link]
up/theme_render/8

Clase RenderElement
[Link]
[Link]/class/RenderElement/8

Caché de arrays renderizables


[Link]

122 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

Elementos renderizables 28.2

En la Unidad 23 estudiamos los elementos de formulario, que son un subconjunto


de los elementos renderizables. En este apartado veremos algunos otros elementos
renderizables que se pueden utilizar fuera de formularios.

Cada elemento renderizable viene definido por una clase que extiende a la clase
RenderElement:

[Link]
[Link]/class/RenderElement/8

Inspeccionando la clase base y la clase específica de cada elemento, descubriremos


todas las propiedades disponibles.

dropbutton

El elemento dropbutton genera un desplegable de acciones o enlaces. Se utiliza


para agrupar operaciones (en los tipos de contenido, en las vistas, etc.) [F28.2a].

La propiedad #links es un array donde se indican los enlaces a las acciones.

$build['dropbutton'] = [
'#type' => 'dropbutton',
'#links' => [
'view' => [
'title' => $this->t('View'),
'url' => Url::fromRoute('forcontu_theming.link_view'),
],
'edit' => [
'title' => $this->t('Edit'),
'url' => Url::fromRoute('forcontu_theming.link_edit'),
],
'delete' => [
'title' => $this->t('Delete'),
'url' => Url::fromRoute('forcontu_theming.link_delete'),
],
],
];

F28.2a
dropbutton
Botón agrupador de
acciones o enlaces.

Clase Dropbutton:
[Link]
[Link]/class/Dropbutton/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 123
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

operations

Este elemento extiende a la clase Dropbutton para añadir la posibilidad de


renderizarlo de forma diferente a través de plantillas.

Clase Operations:
[Link]
.php/class/Operations/8

more_link

El elemento more_link permite añadir un enlace de 'Ver más', muy típico de los
bloques, en los que se muestra un listado limitado de elementos, y un enlace de
"ver más" para ir al listado completo [F28.2b].

La propiedad #title permite modificar el texto del enlace. El valor por defecto es
'More' (Más).

$build['more_link'] = [
'#type' => 'more_link',
'#url' => Url::fromRoute('forcontu_theming.list')
];

F28.2a
more_link
Enlace de "ver más".

Clase MoreLink:
[Link]
hp/class/MoreLink/8

toolbar_item

Se utiliza dentro de una implementación de hook_toolbar() para añadir enlaces


adicionales a la barra de herramientas de administración. No se trata de los
elementos de menú, sino de las opciones adicionales como Mi cuenta, Cerrar
sesión, etc.

Por ejemplo, el módulo Masquerade añade el enlace 'Unmasquerade' para volver


al usuario original.

?php

/**
* Implements hook_toolbar().
*
*/
function masquerade_toolbar() {
$items = array();
if (\Drupal::service('masquerade')->isMasquerading()) {

124 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

$items['masquerade_switch_back'] = array(
'#type' => 'toolbar_item',
'tab' => array(
'#type' => 'link',
'#title' => t('Unmasquerade'),
'#url' => Url::fromRoute('[Link]'),
),
'#weight' => 101,
);
}
return $items;
}

[Link]
hp/class/ToolbarItem/8

html_tag

Permite añadir cualquier etiqueta HTML (#tag), con sus atributos (#attributes) y
valor (#value). Por ejemplo:

$color = '#ffeb3b';
$build['html_tag'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#value' => $this->t('The content area color has been changed to
@code', array('@code' => $color)),
'#attributes' => [
'style' => 'background-color: ' . $color,
],
];

Genera la salida HTML:

<p style="background-color: #ffeb3b">The content area color has


been changed to #ffeb3b</p>

[Link]
hp/class/HtmlTag/8

status_messages

Elemento para mostrar mensajes de estado del sistema. Lo que hace el elemento
es imprimir el resultado de las llamadas a drupal_set_message() que se hayan
realizado durante la ejecución de la página. Los mensajes se almacenan en una
variable de sesión y se muestran en la página a través de la plantilla [Link].
Con este elemento podemos imprimir los mensajes de estado en cualquier otra
ubicación.

$build['status_messages'] = [
'#type' => 'status_messages',
];

Clase StatusMessages:
[Link]
[Link]/class/StatusMessages/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 125
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

view

Renderiza una vista.

$build['view'] = [
'#prefix' => '<div class="view-comments_recent">',
'#suffix' => '</div>',
'#type' => 'view',
'#name' => 'comments_recent',
'#display_id' => 'block_1',
'#embed' => TRUE,
];

[Link]
View/8

inline_template

Elemento renderizable en el que se añade el código de una plantilla Twig inline.

Las propiedades disponibles son:

- #template. Código Twig que hará de plantilla in-line.


- #context. Array con las variables a sustituir en la plantilla. Estas variables
pueden ser una cadena o un array renderizable (elemento hijo que se
renderizará primero y luego se sustituirá su HTML en la plantilla).

$build['inline_template'] = [
'#type' => 'inline_template',
'#template' => '<div class="block-filter-text-source">{{ label
}}</div>',
'#context' => [
'label' => $this->t('Lorem Ipsum'),
],
];

Generará la salida HTML:

<div class="block-filter-text-source">Lorem Ipsum</div>

Clase InlineTemplate:
[Link]
[Link]/class/InlineTemplate/8

126 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

Definición de plantillas en módulos 28.3

Para implementar plantillas en un módulo, tenemos que seguir estos pasos:

- Definir la plantilla implementando hook_theme().


- Crear el archivo de plantilla (.[Link]).
- Hacer uso de la plantilla desde un array renderizable (atributo #theme).

Función hook_theme()

La función hook_theme() permite definir nuevas plantillas en módulos y temas.

[Link]
tion/hook_theme/8

hook_theme($existing, $type, $theme, $path)

Los parámetros de entrada que recibe la función son:

- $existing. Array con información de otras implementaciones de plantilla,


por si pudiera ser necesario extraer información de ellas.

- $type. Indica si se está procesando un módulo, un tema, etc.: 'module',


'base_theme_engine', 'theme_engine', 'base_theme', 'theme'.

- $theme. Nombre del módulo o tema que se está procesando.

- $path. Ruta al directorio del módulo o tema que se está procesando.

El valor devuelto es un array donde cada elemento es un array que describe una
plantilla. La clave identifica al elemento, y se conoce como "theme hook". Algunas
de las propiedades que podemos usar al definir una plantilla son:

- variables. Solo se utiliza para arrays renderizables que hacen referencia


a la plantilla con la propiedad #theme. Este es el formato que utilizaremos
en este primer ejemplo. Se define como un array donde la clave es el
nombre de la variable y el valor es su valor por defecto.

- template. Indica el nombre del archivo de plantilla, sin especificar


la extensión, .[Link], que se añade por el motor de plantillas. Si no se
especifica esta propiedad, el nombre del archivo de plantilla se asigna
automáticamente a partir del valor de la clave del elemento. Por ejemplo,
para el elemento 'forcontu_theming_dimensions', se asignará la plantilla
'forcontu-theming-dimensions'.

Recuerda que en Drupal 8 ya no usamos funciones de tema, solo archivos


de plantilla.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 127
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

Vamos a definir el elemento de plantilla forcontu_theming_dimensions,


asociado al archivo de plantilla [Link]. Las
variables disponibles serán 'length', 'width', 'height' y 'unit'.

/**
* Implements hook_theme().
*/
function forcontu_theming_theme($existing, $type, $theme, $path) {
return [
'forcontu_theming_dimensions' => [
'variables' => [
'length' => NULL,
'width' => NULL,
'height' => NULL,
'unit' => 'cm.'
],
'template' => 'forcontu-theming-dimensions',
],
];
}

A continuación, crearemos el archivo de plantilla en:

/forcontu_theming/templates/[Link]

{#
/**
* @file
* Default theme implementation for 'forcontu_theming_dimensions'.
*
* Available variables:
* - length.
* - width.
* - height
* - unit (dafault to cm.)
*/
#}
<span class="item-dimensions">
{% trans %}
Dimensions (length x width x height): {{ length }} x {{ width }} x
{{ height }} {{ unit }}
{% endtrans %}
</span>

Recuerda que siempre hay que traducir las cadenas incluidas directamente en las
plantillas.

Ya tenemos definida la plantilla y podemos hacer uso de ella desde cualquier array
renderizable. Vacía la caché del sitio para que el sistema reconozca la nueva
plantilla.

En el atributo #theme del array renderizable escribiremos el nombre del elemento


de plantilla (forcontu_theming_dimensions), y no el nombre del archivo de plantilla.

$build['item_dimensions'] = [
'#theme' => 'forcontu_theming_dimensions',
'#length' => 12,
'#width' => 8,
'#height' => 24,
];

128 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

La salida HTML de este elemento será:

<span class="item-dimensions">
Dimensions (length x width x height): 12 x 8 x 24 cm.
</span>

Para ampliar sobre este tema, consulta las implementaciones de hook_theme() de


los módulos del núcleo (.module), y los archivos de plantilla (/templates). El módulo
Forum es un buen ejemplo de definición y uso de plantillas.

Funciones de preprocesamiento

En el apartado 27.4 estudiamos las funciones de preprocesamiento en temas. Las


funciones de preprocesamiento nos permiten interceptar y modificar las variables
de una plantilla, antes de que sea renderizada. Se pueden utilizar para modificar
plantillas de otros módulos, pero también para modificar las plantillas definidas en
el mismo módulo.

La implementación es similar a la estudiada para temas, pero utilizando el nombre


del módulo en lugar del tema:

- MODULE_preprocess(&$variables, $hook). Es la implementación de


hook_preprocess() dentro de un módulo. Esta función será llamada para
todas las plantillas.

- MODULE_preprocess_HOOK(&$variables). Es la implementación de
hook_preprocess_HOOK() dentro de un módulo. Esta función se utiliza
cuando un módulo desea modificar las variables de la plantilla HOOK de
otro módulo.

Las funciones de preprocesamiento de módulos se ejecutan antes que las de temas


(repasa el orden completo en el apartado 27.4).

Añadir librerías

En el apartado 27.1 vimos cómo definir y añadir una librería a un tema. Un módulo
también puede añadir su propia librería de estilos (CSS) o código Javascript.
Seguiremos los siguientes pasos:

1. Crear el archivo CSS, definir los estilos, y guardarlo en la carpeta /css del
módulo.

En nuestro ejemplo crearemos el archivo /css/forcontu_theming.css, con


el siguiente estilo, para mostrar las dimensiones en rojo:

.item-dimensions {
color: #FF0000;
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 129
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

2. Definir la librería. Las librerías se definen en el archivo *.[Link], en


la carpeta raíz del módulo. Para nuestro ejemplo, crearemos el archivo
forcontu_theming.[Link], con el siguiente contenido:

forcontu_theming.css:
version: 1.x
css:
theme:
css/forcontu_theming.css: {}

Con 'theme' estamos estableciendo el peso o nivel del estilo. Los valores
posibles, por orden de peso, de menor a mayor, son: base, layout,
component, state y theme.

3. Una vez definida la librería, tenemos varias formas de utilizarla:

a. Añadir la librería a un array renderizable:

$build['item_dimensions'] = [
'#theme' => 'forcontu_theming_dimensions',
'#attached' => [
'library' => [
'forcontu_theming/forcontu_theming.css',
],
],
'#length' => 12,
'#width' => 8,
'#height' => 24,
];

El nombre indicado es el nombre del módulo más el nombre de la


librería (no se especifica el nombre del archivo CSS directamente).

b. Adjuntar la librería a una plantilla twig. Utilizamos la función de twig


attach_library(), indicando también el nombre del módulo y el de la
librería.

{{ attach_library('forcontu_theming/forcontu_theming.css') }}
<span class="item-dimensions">
{% trans %}
Dimensions (length x width x height): {{ length }} x {{ width }} x
{{ height }} {{ unit }}
{% endtrans %}
</span>

c. A través de una función de preprocesamiento:

/**
* Implements MODULE_preprocess_HOOK() for focontu_theming_dimensions.
*/
function forcontu_theming_preprocess_forcontu_theming_dimensions(&$variables) {
$variables['#attached']['library'][] = 'forcontu_theming/forcontu_theming.css';
}

d. Añadiendo la librería a ciertas páginas. Si queremos limitar las


páginas en las que se mostrará, podemos hacerlo también a través
de la función de preprocesamiento, pero a nivel de página
MODULE_preprocess_page(). En este caso tenemos que añadir

130 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

caché según la ruta, y comprobar la ruta actual. Por ejemplo:

/**
* Implements MODULE_preprocess_HOOK() for page.
*/
function forcontu_theming_preprocess_page(&$variables) {
$variables['page']['#cache']['contexts'][] = 'route';
if (\Drupal::routeMatch()->getRouteName() === 'forcontu_theming.render_elements') {
$variables['#attached']['library'][] = 'forcontu_theming/forcontu_theming.css';
}
}

En todos los casos, el resultado será el texto en rojo, como se muestra en la Figura
[F28.3a]:

F28.3a
Librería CSS
Estilos aplicados a través
de un archivo CSS del
módulo.
A nivel de HTML, el sistema está cargando la librería CSS de esta forma:

@import url("/modules/custom/forcontu_theming/css/forcontu_theming.css?omyl8u");

Si quieres saber más sobre cómo trabajar con librerías en módulos y temas, puedes
consultar este enlace:

[Link]
and-javascript-js-to-a-drupal-8-module

Sugerencias de plantillas (Template suggestions)

Las sugerencias de plantillas estudiadas para temas, también se aplican para las
plantillas creadas en los módulos. Desde el módulo podemos implementar
hook_theme_suggestions_alter() para añadir nuevas sugerencias de nombres de
plantillas a las plantillas creadas por el módulo.

Como ejemplo, añadimos una sugerencia de nombre que actúa únicamente cuando
el usuario está logueado.

/**
* Implements hook_theme_suggestions_HOOK_alter() for HOOK
* 'forcontu_theming_dimensions'.
*/
function
forcontu_theming_theme_suggestions_forcontu_theming_dimensions_alter(array
&$suggestions, array $variables) {

if (\Drupal::currentUser()->isAuthenticated()) {
$suggestions[] = 'forcontu_theming_dimensions__logged_in';
}
}

Si tenemos la depuración de Twig activada, en el código HTML generado se


mostrará la plantilla actual y las sugerencias.

<!-- THEME DEBUG -->


<!-- THEME HOOK: 'forcontu_theming_dimensions' -->

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 131
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

<!-- FILE NAME SUGGESTIONS


* [Link]
x [Link]
-->
<!-- BEGIN OUTPUT from
'modules/custom/forcontu_theming/templates/forcontu-theming-
[Link]' -->
<span class="item-dimensions">
Dimensions (length x width x height): 12 x 8 x 24 cm.
</span>
<!-- END OUTPUT from
'modules/custom/forcontu_theming/templates/forcontu-theming-
[Link]' -->

Para hacer uso de la plantilla con el nombre sugerido, copiaremos la plantilla al


tema, y la renombraremos.

Crear nuevos elementos renderizables

En el ejemplo anterior creamos una plantilla que podemos utilizar en un array


renderizable, con la propiedad #theme. En un módulo también podemos definir
nuevos tipos de elementos renderizables, que se corresponderán con la propiedad
#type del array renderizable.

Si se trata de un elemento de uso general, extenderemos la clase RenderElement.


Si el elemento es de formulario, extenderemos la clase FormElement (que a su vez
extiende a RenderElement).

[Link]
[Link]/class/RenderElement/8

[Link]
[Link]/class/FormElement/8

También es posible extender la clase de un tipo de elemento ya existente, si solo


necesitamos incorporar alguna propiedad adicional. Por ejemplo, el elemento
MachineName extiende a la clase Textfield, que es la que extiende a FormElement.

Los nuevos elementos se definen en un archivo PHP con el nombre de la clase,


dentro de la carpeta Element. En nuestro módulo crearemos el archivo
forcontu_theming/src/Element/[Link], teniendo en
cuenta:

- Nuestro elemento extenderá a la clase RenderElement.


- El tipo de elemento se registra mediante Annotations, a través de la
directiva @RenderElement.
- El método getInfo() devuelve las propiedades del elemento. En nuestro
caso, reutilizamos la plantilla creada anteriormente, que asignamos a la
propiedad #theme.
- Propiedad #pre_render. Es un array con funciones (generalmente
métodos de la misma clase) que serán llamadas justo antes de que el
elemento sea renderizado. El argumento de entrada es $element, y el
valor devuelto es el mismo $element pero modificado. Esta función es
equivalente a las funciones de preprocesamiento, aunque éstas se utilizan

132 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

generalmente para actuar sobre elementos de otros módulos.

En nuestro ejemplo hemos añadido la función de pre-renderizado, aunque


no hemos realizado ninguna modificación sobre $element.

<?php

namespace Drupal\forcontu_theming\Element;

use Drupal\Core\Render\Element\RenderElement;

/**
* Provides a render element to display a Dimensions item.
*
* @RenderElement("forcontu_theming_dimensions")
*/
class ForcontuThemingDimensions extends RenderElement {

/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
'#pre_render' => [
[$class, 'preRenderForcontuThemingDimensions'],
],
'#length' => NULL,
'#width' => NULL,
'#height' => NULL,
'#unit' => 'cm.',
'#theme' => 'forcontu_theming_dimensions',
];
}

/**
* Element pre render callback.
*/
public static function preRenderForcontuThemingDimensions($element) {
return $element;
}
}

Al utilizar este tipo de elemento, indicaremos su nombre en la propiedad #type,


así como el resto de propiedades definidas en el método getInfo():

$build['item_dimensions_element'] = [
'#type' => 'forcontu_theming_dimensions',
'#length' => 11,
'#width' => 7,
'#height' => 23,
'#unit' => 'mm.',
];

Como hemos utilizado el mismo archivo de plantilla, el HTML generado será


exactamente el mismo.

Para ver más ejemplos de creación de elementos renderizables, localiza en el


núcleo y otros módulos contribuidos, las clases que extienden a RenderElement y
FormElement.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 133
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

28.4 Crear extensiones de Twig

En el apartado 27.3 vimos algunas extensiones (filtros y funciones) que podemos


utilizar en las plantillas Twig. Algunas de estas extensiones son genéricas de Twig,
y otras han sido implementadas específicamente para Drupal.

En este apartado vamos a ver cómo crear extensiones personalizadas en un


módulo.

Las extensiones extienden la clase \Twig_Extension y se definen en un archivo PHP


con el nombre de clase, ubicado en la carpeta src/TwigExtension del módulo.

[Link]
wig_Extension/8

En nuestro módulo de ejemplo, vamos a añadir las siguientes extensiones de Twig:

- loripsum($length = 50). Es una función que devuelve un texto Lorem


Ipsum, obtenido de la API de [Link]. Como parámetro podemos
indicar el número de caracteres del texto.

- space_replace. Filtro que sustituye los espacios por guiones en la


cadena donde se aplique.

<?php

namespace Drupal\forcontu_theming\TwigExtension;

class ForcontuThemingTwigExtension extends \Twig_Extension {

public function getFunctions() {


return [
new \Twig_SimpleFunction('loripsum', [$this, 'loripsum']),
];
}

public function getFilters() {


return [
new \Twig_SimpleFilter('space_replace', [$this, 'spaceReplace']),
];
}

public function getName() {


return 'forcontu_theming.[Link]';
}

public function loripsum($length = 50) {


return
substr(file_get_contents('[Link]
, 0, $length) . '.';
}

public function spaceReplace($string) {


return str_replace(' ', '-', $string);
}
}
El método getName() devuelve el nombre de la extensión.

134 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

El método getFunctions() devuelve las funciones implementadas por esta clase,


creando para cada función un objeto de tipo \Twig_SimpleFunction, al que se le
pasa como parámetros el nombre que tendrá la función en Twig (loripsum()), y el
nombre de la función o método de callback ($this->loripsum()), encargada de
devolver el valor de la función.

El método getFiters() devuelve los filtros implementados por la clase. También se


indica en el objeto \Twig_SimpleFilter, el nombre del filtro en Twig (space_replace)
y el nombre de la función de callback ($this->spaceReplace()).

Por último, necesitamos registrar el servicio en el archivo .[Link] del módulo,


que irá en el raíz (forcontu_theming.[Link]):

services:
forcontu_theming.[Link]:
class:
Drupal\forcontu_theming\TwigExtension\ForcontuThemingTwigExtension
tags:
- { name: [Link] }

Ahora ya podemos hacer uso de las nuevas extensiones en cualquier plantilla Twig:

<p>{{ loripsum(300) }}</p>

<p>{{ loripsum(100)|space_replace }}</p>

Siendo el resultado HTML:

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quos


quidem tibi studiose et diligenter tractandos magnopere censeo.
Ita enim vivunt quidam, ut eorum vita refellatur oratio. Atqui
eorum nihil est eius generis, ut sit in fine atque extrerno
bonorum. Duo Reges: constructio interrete. Immo sit.</p>

<p>Lorem-ipsum-dolor-sit-amet,-consectetur-adipiscing-elit.-Quin-
etiam-ferae,-inquit-Pacuvius,-quíbus-.</p>

En el ejemplo del siguiente apartado, generado con Drupal Console, veremos cómo
inyectar servicios dentro de la clase que implementa las extensiones.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 135
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

28.5 Hazlo desde la consola

drupal generate:twig:extension

Genera una extensión de Twig.

drupal generate:twig:extension options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:twig:extension

// Welcome to the Drupal Twig Extension generator


Enter the module name [admin_toolbar]:
> forcontu_console

Enter the Twig extension name [forcontu_console.[Link]]:


>

Class name [DefaultTwigExtension]:


> test

Do you want to load services from the container (yes/no) [no]:


> yes

Type the service name or use keyup or keydown.


This is optional, press enter to continue

Enter your service [ ]:


> current_user
Enter your service [ ]:
>

Do you confirm generation? (yes/no) [yes]:


> yes

// cache:rebuild

Rebuilding cache(s), wait a moment please.

[OK] Done clearing cache(s).

Generated or updated files

1 - modules/custom/forcontu_console/forcontu_console.[Link]
2 - modules/custom/forcontu_console/src/TwigExtension/[Link]

[Link]
console/content/es/commands/[Link]

A continuación, se muestra la clase generada:

<?php

namespace Drupal\forcontu_console\TwigExtension;

use Drupal\Core\Render\Renderer;
use Drupal\Core\Session\AccountProxy;

136 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

/**
* Class test.
*
* @package Drupal\forcontu_console
*/
class test extends \Twig_Extension {

/**
* Drupal\Core\Session\AccountProxy definition.
*
* @var \Drupal\Core\Session\AccountProxy
*/
protected $currentUser;

/**
* Constructor.
*/
public function __construct(Renderer $renderer, AccountProxy
$current_user) {
parent::__construct($renderer);
$this->currentUser = $current_user;
}

/**
* {@inheritdoc}
*/
public function getTokenParsers() {
return [];
}

/**
* {@inheritdoc}
*/
public function getNodeVisitors() {
return [];
}

/**
* {@inheritdoc}
*/
public function getFilters() {
return [];
}

/**
* {@inheritdoc}
*/
public function getTests() {
return [];
}

/**
* {@inheritdoc}
*/
public function getFunctions() {
return [];
}

/**
* {@inheritdoc}
*/
public function getOperators() {
return [];
}

/**
* {@inheritdoc}
*/
public function getName() {
return 'forcontu_console.[Link]';
}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 137
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 28. Theming II: Theming en módulos

También se ha generado el archivo de servicios: forcontu_console.[Link]:

services:
forcontu_console.[Link]:
class: Drupal\forcontu_console\TwigExtension\test
arguments: ['@renderer', '@current_user']
tags:
- { name: [Link] }

138 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

29 Plugins II: Tipos de plugins y servicios


En unidades anteriores hemos visto cómo instanciar plugins de tipos
Comparativa D8/D7
ya definidos y utilizar servicios de otros módulos. En esta unidad
vamos a ver cómo crear nuevos tipos de plugins y nuevos servicios. Los plugins y servicios son componentes
nuevos en Drupal 8.

29
Contenidos de la Unidad
29.1 Crear un tipo de plugin
29.2 Crear un servicio personalizado
29.3 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 141
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

29.1 Crear un tipo de plugin

Los plugins se pueden considerar piezas o componentes adicionales que podemos


integrar en el sistema para añadir una funcionalidad específica. Los plugins que
desarrollan una misma funcionalidad, se generan o instancian a partir de un mismo
tipo de plugin.

Algunos de los tipos de plugin disponibles en el núcleo de Drupal son los bloques,
los efectos de imagen, los tipos de campo o los controles y formateadores de
campos.

Cada tipo de plugin tiene su propio servicio "plugin manager", que es una clase
que implementa la interfaz PluginManagerInterface y que se encarga de definir qué
método se utilizará para descubrir o localizar los plugins de ese tipo que se hayan
creado en el sistema (annotation, hook, YAML, etc.), y cómo se crearán o
instanciarán plugins de ese tipo.

Generalmente los tipos de plugin incluyen una clase base para facilitar la
instanciación de plugins. Por ejemplo, para crear un plugin de Bloque, crearemos
una clase que extienda a la clase BlockBase.

Los plugins basados en Annotations utilizan esta nomenclatura para ser


registrados en el sistema y descubiertos por el plugin manager. Cada tipo de plugin
utilizará sus propias directivas (@Block para bloques, @FieldFormatter para
formateadores de campos, etc.).

[Link]

Para definir un nuevo tipo de plugin, tenemos que seguir estos pasos:

1. Definir una clase gestora del plugin (plugin manager).

2. Definir el servicio que apunta a la clase plugin manager.

3. Definir la clase annotation que facilita al sistema información sobre el tipo


de plugin.

4. Definir una interfaz que tendrán que implementar los plugins de este tipo.

5. Crear una clase base que implemente la interfaz, y que será la clase de la
que heredarán los plugins de este tipo.

6. Por último, ya podemos crear nuevos plugins de este tipo, extendiendo la


clase base.

7. Para utilizar los plugins de este tipo, podemos llamar al servicio encargado
de gestionarlo. Tendremos que crear una instancia del plugin que
vayamos a utilizar.

En esta unidad vamos a implementar el módulo Forcontu Plugins

142 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

(forcontu_plugins), donde definiremos un tipo de plugin llamado Fipsum. El


objetivo del plugin es generar un texto de prueba tipo Lorem Ipsum, que podrá
venir desde diferentes fuentes. Cada una de esas fuentes será una implementación
distinta del tipo de plugin Fipsum. Vamos a construir este ejemplo siguiendo los
pasos indicados:

Paso 1. Plugin Manager

Comenzamos creando el archivo /forcontu_plugins/src/[Link].


La clase FipsumPluginManager extiende a DefaultPluginManager, y será la
encargada de gestionar los plugins de tipo Fipsum. Esta clase también se encarga
de indicar al sistema cómo se descubrirán o localizarán los plugins definidos. Si no
se especifica otro método, se utilizará annotations.

<?php

namespace Drupal\forcontu_plugins;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\forcontu_plugins\Annotation\Fipsum;

/**
* Provides the plugin manager for Fipsum plugins.
*/
class FipsumPluginManager extends DefaultPluginManager {

public function __construct(\Traversable $namespaces,


CacheBackendInterface $cache_backend, ModuleHandlerInterface
$module_handler) {

$subdir = 'Plugin/Fipsum';
$plugin_interface = FipsumInterface::class;
$plugin_definition_annotation_name = Fipsum::class;

parent::__construct($subdir, $namespaces, $module_handler,


$plugin_interface, $plugin_definition_annotation_name);

$this->alterInfo('forcontu_plugins_fipsum_info');
$this->setCacheBackend($cache_backend, 'forcontu_plugins_fipsum_info');
}
}

Entre las cláusulas use, debemos especificar la clase de tipo Annotation, que
definiremos en el paso 3:

use Drupal\forcontu_plugins\Annotation\Fipsum;

La clase plugin manager requiere llamar a la clase constructora de la clase padre


(DefaultPluginManager). Entre los parámetros que se requiere, debemos establecer
los siguientes valores:

- $subdir. Subdirectorio donde se crearán los plugins de este tipo.


- $plugin_interface. La interfaz que debe implementar cada plugin de
este tipo.
- $plugin_definition_annotation_name. El nombre de la clase
annotation que contiene la definición del plugin.
Puedes consultar la clase DefaultPluginManager en:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 143
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

[Link]
[Link]/class/DefaultPluginManager/8

Paso 2. Registrar el servicio

La clase FipsumPluginManager debe ser registrada como servicio en el


archivo .[Link]. En nuestro ejemplo, el servicio ha sido identificado como
'[Link]'. Se debe indicar la clase plugin manager y el servicio de
la clase padre (default_plugin_manager).

services:
[Link]:
class: Drupal\forcontu_plugins\FipsumPluginManager
parent: default_plugin_manager

Paso 3. Annotation

El tipo de plugin definido utiliza el método Annotation para que el sistema pueda
descubrir sus plugins. Para definir la clase annotation, extendemos a la clase
Drupal\Component\Annotation\Plugin. La clase Fipsum se creará en el archivo
src/Annotation/[Link]. Dentro del comentario annotation tenemos que añadir
la propiedad @Annotation. Dentro de la clase definimos los atributos que tendrá el
plugin.

<?php

namespace Drupal\forcontu_plugins\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines a Fipsum annotation object.
*
* @see \Drupal\forcontu_plugins\FipsumPluginManager
* @see plugin_api
*
* @Annotation
*/
class Fipsum extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The description of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $description;
}

144 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Paso 4. Definir la interfaz del tipo de plugin

Debemos definir una interfaz que tendrán que implementar todos los plugins de
este tipo. En nuestro tipo de plugin Fipsum, definimos dos métodos:

- description(). Devuelve la descripción del plugin.


- generate($length). Devuelve el texto de prueba generado por el plugin,
limitado a la cantidad de caracteres establecida en $length.

Archivo: /forcontu_plugins/src/[Link]
<?php

namespace Drupal\forcontu_plugins;

/**
* Interface for all Fipsum type plugins.
*/
interface FipsumInterface {

public function description();

public function generate($length);


}

Paso 5. Definir la clase base

A continuación, creamos la clase base FipsumBase, que extiende PluginBase e


implementa la interfaz definida anteriormente, FipsumInterface.

Esta clase base será la que extenderán los plugins de este tipo.

Archivo: /forcontu_plugins/src/[Link]
<?php

namespace Drupal\forcontu_plugins;

use Drupal\Component\Plugin\PluginBase;

abstract class FipsumBase extends PluginBase implements


FipsumInterface {

public function description() {


return $this->pluginDefinition['description'];
}

abstract public function generate($length);

Paso 6. Crear nuevos plugins

Como indicamos en FipsumPluginManager, los plugins de este tipo se deben crear


en src/Plugin/Fipsum. Cada plugin vendrá definido por una clase que extiende a
FipsumBase.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 145
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Además, cada plugin debe llevar su identificación en forma de Annotation.

Plugin lorem_ipsum

Archivo: src/Plugin/Fipsum/[Link]
Genera un texto tipo Lorem Ipsum clásico, obtenido a través de la API de
[Link].

<?php

namespace Drupal\forcontu_plugins\Plugin\Fipsum;

use Drupal\forcontu_plugins\FipsumBase;

/**
* Provides a LoremIpsum text.
*
* @Fipsum(
* id = "lorem_ipsum",
* description = @Translation("Lorem Ipsum text")
* )
*/
class LoremIpsum extends FipsumBase {

public function generate($length = 100) {


return
substr(file_get_contents('[Link]
text'), 0, $length) . '.';
}
}

Plugin forcontu_ipsum

Archivo: src/Plugin/Fipsum/[Link]
Genera un texto tipo Forcontu Ipsum, que se obtiene a partir de una URL extraída
de [Link].

<?php

namespace Drupal\forcontu_plugins\Plugin\Fipsum;

use Drupal\forcontu_plugins\FipsumBase;

/**
* Provides an ForcontuIpsum text.
*
* @Fipsum(
* id = "forcontu_ipsum",
* description = @Translation("Forcontu Ipsum text")
* )
*/
class ForcontuIpsum extends FipsumBase {

public function generate($length = 100) {

$content = preg_replace('#<[^>]+>#', ' ',


file_get_contents('[Link]
$content = preg_replace('/\s+/', ' ', $content);
$content = preg_replace('/[0-9\,\(\)]+/', '', $content);
$content_array = explode(' ', $content);
shuffle($content_array);

146 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

return 'Forcontu ipsum ' . substr(implode(' ',


$content_array), 0, $length) . '.';
}
}

Paso 7. Utilizar el servicio

Para utilizar los plugins de este tipo, debemos llamar al servicio encargado de
gestionarlo, y crear una instancia del plugin que vayamos a utilizar.

Podemos llamar al servicio de cualquiera de las formas estudiadas,


recomendándose, siempre que sea posible, la inyección de servicios en la clase
donde se vaya a usar.

Para probar el funcionamiento de los plugins creados, implementaremos la ruta


con URL /forcontu/plugins/fipsum (archivo forcontu_plugins.[Link]).

forcontu_plugins.fipsum:
path: '/forcontu/plugins/fipsum'
defaults:
_controller:
'\Drupal\forcontu_plugins\Controller\ForcontuPluginsController::fi
psum'
_title: 'Fipsum Test Page'
requirements:
_permission: 'access content'

Inyectamos el servicio [Link], y creamos las instancias de cada


plugin con el método createInstance, indicando el ID de cada plugin.

<?php

/**
* @file
* Contains
\Drupal\forcontu_plugins\Controller\ForcontuPluginsController.
*/

namespace Drupal\forcontu_plugins\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\forcontu_plugins\FipsumPluginManager;

class ForcontuPluginsController extends ControllerBase {

protected $fipsum;

public function __construct(FipsumPluginManager $fipsum) {


$this->fipsum = $fipsum;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('[Link]')
);
}

public function fipsum() {


$lorem_ipsum = $this->fipsum->createInstance('lorem_ipsum');

$build['fipsum_lorem_ipsum_title'] = [

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 147
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

'#markup' => '<h2>' . $lorem_ipsum->description() . '</h2>',


];

$build['fipsum_lorem_ipsum_text'] = [
'#markup' => '<p>' . $lorem_ipsum->generate(300) . '</p>',
];

$forcontu_ipsum = $this->fipsum->createInstance('forcontu_ipsum');

$build['fipsum_forcontu_ipsum_title'] = [
'#markup' => '<h2>' . $forcontu_ipsum->description() . '</h2>',
];

$build['fipsum_forcontu_ipsum_text'] = [
'#markup' => '<p>' . $forcontu_ipsum->generate(600) . '</p>',
];

return $build;
}
}

Los textos devueltos se muestran en la Figura [F]

F29.1a
Plugins
Texto Lorem ipsum
devuelto por los plugins de
tipo Fipsum creados.

Enlaces de interés:

- Annotations-based plugins
[Link]

- Unraveling the Drupal 8 Plugin System


[Link]

- Plugin API (documentación oficial):


[Link]

- Plugin API overview:


[Link]

- Creating your own Plugin Manager:


[Link]
manager

148 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Crear un servicio personalizado 29.2

Ya hemos trabajado con servicios y sabemos cómo hacer uso de ellos. En este
apartado vamos a ver cómo se crea un nuevo servicio personalizado.

Un servicio es una forma de encapsular una funcionalidad y hacer que esté


disponible desde el contenedor de servicios. Por tanto, solo necesitaremos una
clase específica para el servicio (con su correspondiente interfaz), y dar de alta esa
clase en el archivo [Link] del módulo.

Seguimos trabajando en el módulo Forcontu Plugins, donde crearemos el servicio


[Link], que devuelve un listado de cursos disponibles en Forcontu.

Paso 1. Registrar el servicio en el archivo [Link]

Comenzamos registrando la clase del servicio en el archivo .[Link]. El servicio


se llamará [Link] y será gestionado por la clase ForcontuCourses.

El listado de cursos lo pasaremos como parámetro estático, definiéndolo


directamente en el archivo .[Link]. El argumento entre %
("%[Link]%") indica que se trata de un parámetro, cuyo valor puede
definirse en la sección parameters del archivo .[Link].

Nota: La sección parameters se coloca al mismo nivel que services, y no dentro


del servicio que estamos definiendo.

services:
[Link]:
class: Drupal\forcontu_plugins\ForcontuCourses
arguments: ["%[Link]%"]

parameters:
[Link]:
- { title: 'Experto en Drupal 8 Site Building',
tutor: 'Fran Gil', duration: 4, hours: 180}
- { title: 'Experto en Drupal 8 Back-End Development',
tutor: 'Fran Gil', duration: 6, hours: 300}
- { title: 'Experto en Drupal 8 Front-End Development',
tutor: 'Fran Gil', duration: 4, hours: 180}
- { title: 'Máster en Drupal 8',
tutor: 'Fran Gil', duration: 14, hours: 660}

Más información sobre las propiedades que puede tener un archivo [Link]:

[Link]
of-a-service-file

Paso 2. Crear la intefaz

Crearemos en archivo src/[Link] para implementar la


interfaz. Solo será necesario el método getCourses(), que devuelve el listado de
cursos definido anteriormente.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 149
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

<?php

namespace Drupal\forcontu_plugins;

interface ForcontuCoursesInterface {

public function getCourses();

Paso 3. Crear la clase del servicio

La clase ForcontuCourses se define en el archivo src/[Link], e


implementa la interfaz ForcontuCoursesInteraface.

El constructor de la clase recibe como parámetro $courses, que tomará su valor


del valor por defecto del servicio, definido en el archivo .[Link].

<?php

namespace Drupal\forcontu_plugins;

class ForcontuCourses implements ForcontuCoursesInterface {

protected $courses;

public function __construct($courses) {


$this->courses = $courses;
}

public function getCourses() {


return $this->courses;
}

Paso 4. Utilizar el servicio

El servicio se utilizará como cualquier otro servicio. Como ejemplo, inyectamos el


servicio en la clase controladora ForcontuPluginsController, y creamos una nueva
página.

Archivo forcontu_plugins.[Link]:

forcontu_plugins.courses:
path: '/forcontu/plugins/courses'
defaults:
_controller:
'\Drupal\forcontu_plugins\Controller\ForcontuPluginsController::co
urses'
_title: 'List of courses'
requirements:
_permission: 'access content'

150 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Archivo src/Controller/[Link]:

<?php

namespace Drupal\forcontu_plugins\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\forcontu_plugins\FipsumPluginManager;
use Drupal\forcontu_plugins\ForcontuCoursesInterface;
class ForcontuPluginsController extends ControllerBase {

protected $fipsum;
protected $forcontuCourses;

public function __construct(FipsumPluginManager $fipsum,


ForcontuCoursesInterface $forcontu_courses) {
$this->fipsum = $fipsum;
$this->forcontuCourses = $forcontu_courses;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('[Link]'),
$container->get('[Link]')
);
}

public function courses() {


$list = $this->forcontuCourses->getCourses();

$header = [$this->t('Title'), $this->t('Tutor'),


$this->t('Duration (months)'), $this->t('Hours')];

$build['forcontu_plugins_table'] = [
'#type' => 'table',
'#header' => $header,
'#rows' => $list,
];

return $build;
}
}

El resultado al cargar la página se muestra en la Figura [F29.2a].

F29.2a
Servicio
[Link]
Página mostrando los
cursos devueltos por el
servicio.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 151
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Inyección de servicios en servicios

Dentro de la clase que define el servicio, podemos inyectar los servicios que
vayamos a utilizar. Para añadir un servicio tenemos que:

- Definir las propiedades y crear el método constructor, como hemos hecho


en otras ocasiones.

<?php

namespace Drupal\forcontu_console;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\Core\Session\AccountProxy;

class ForcontuConsoleFoo implements ForcontuConsoleFooInterface {

protected $database;
protected $currentUser;

public function __construct(Connection $database, AccountProxy $current_user) {


$this->database = $database;
$this->currentUser = $current_user;
}
}

- Añadir los argumentos en la definición del servicio. Cuando al argumento


se le antepone un @, estamos indicando que se trata de un servicio, por
lo que el nombre del argumento debe coincidir con un servicio válido.

services:
forcontu_console.foo:
class: Drupal\forcontu_console\ForcontuConsoleFoo
arguments: ['@database', '@current_user']

152 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Hazlo desde la consola 29.3

Crear un tipo de plugin (Drupal Console)

drupal generate:plugin:type:annotation

Genera un tipo de plugin usando el método annotation.

drupal generate:plugin:type:annotation options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:plugin:type:annotation

// Welcome to the Drupal Plugin Type generator


Enter the module name [admin_toolbar]:
> forcontu_console

Plugin type class name [ExamplePlugin]:


> ForcontuConsoleBar

Plugin type machine name [forcontu_console_bar]:


>

Plugin type label [Forcontu console bar]:


>

Generated or updated files

1 -
modules/custom/forcontu_console/src/Annotation/ForcontuConsoleBar.
php
2 -
modules/custom/forcontu_console/src/Plugin/ForcontuConsoleBarBase.
php
3 -
modules/custom/forcontu_console/src/Plugin/ForcontuConsoleBarInter
[Link]
4 -
modules/custom/forcontu_console/src/Plugin/ForcontuConsoleBarManag
[Link]
5 - modules/custom/forcontu_console/forcontu_console.[Link]

[Link]
console/content/es/commands/[Link]

A continuación, se muestra la clase plugin manager generada:

<?php

namespace Drupal\forcontu_console\Plugin;

use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;

/**
* Provides the Forcontu console bar plugin manager.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 153
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

*/
class ForcontuConsoleBarManager extends DefaultPluginManager {

/**
* Constructor for ForcontuConsoleBarManager objects.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the
root paths
* keyed by the corresponding namespace to look for plugin
implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface
$cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
$module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(\Traversable $namespaces,
CacheBackendInterface $cache_backend, ModuleHandlerInterface
$module_handler) {
parent::__construct('Plugin/ForcontuConsoleBar', $namespaces,
$module_handler,
'Drupal\forcontu_console\Plugin\ForcontuConsoleBarInterface',
'Drupal\forcontu_console\Annotation\ForcontuConsoleBar');

$this-
>alterInfo('forcontu_console_forcontu_console_bar_info');
$this->setCacheBackend($cache_backend,
'forcontu_console_forcontu_console_bar_plugins');
}
}

La interfaz generada:

<?php

namespace Drupal\forcontu_console\Plugin;

use Drupal\Component\Plugin\PluginInspectionInterface;

/**
* Defines an interface for Forcontu console bar plugins.
*/
interface ForcontuConsoleBarInterface extends
PluginInspectionInterface {

// Add get/set methods for your plugin type here.


}

La clase base que extenderán los plugins de este tipo:

<?php

namespace Drupal\forcontu_console\Plugin;

use Drupal\Component\Plugin\PluginBase;

/**
* Base class for Forcontu console bar plugins.
*/
abstract class ForcontuConsoleBarBase extends PluginBase
implements ForcontuConsoleBarInterface {
// Add common methods and abstract methods for your plugin type here.
}

154 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Y la clase annotation con información de localización del plugin:

<?php

namespace Drupal\forcontu_console\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
* Defines a Forcontu console bar item annotation object.
*
* @see \Drupal\forcontu_console\Plugin\ForcontuConsoleBarManager
* @see plugin_api
*
* @Annotation
*/
class ForcontuConsoleBar extends Plugin {

/**
* The plugin ID.
*
* @var string
*/
public $id;

/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}

También se ha generado (o actualizado) el archivo de servicios:


forcontu_console.[Link]:

services:
[Link].forcontu_console_bar:
class: Drupal\forcontu_console\Plugin\ForcontuConsoleBarManager
parent: default_plugin_manager

drupal generate:plugin:type:yaml

Como el comando anterior, también genera un tipo de plugin, pero esta vez con el
método de descubrimiento yaml, en lugar de annotation. Prueba el comando para
ver las diferentes entre un método y otro.

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 155
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Otros comandos de creación de plugins (Drupal Console)

drupal generate:plugin:*

En la documentación de Drupal Console encontrarás otros comandos de generación


de plugins de tipos ya definidos. Por ejemplo:

- drupal generate:plugin:block, para generar un plugin de bloque.


- drupal generate:plugin:ckeditorbutton, para generar un plugin de
botón de CKEditor.
- drupal generate:plugin:imageeffect, para generar un plugin de efecto
de imagen.

Crear un servicio (Drupal Console)

drupal generate:service

Genera un servicio.

drupal generate:service options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:service

// Welcome to the Drupal service generator


Enter the module name [admin_toolbar]:
> forcontu_console

Enter the service name [forcontu_console.default]:


> forcontu_console.foo

Enter the Class name [DefaultService]:


> ForcontuConsoleFoo

Create an interface (yes/no) [yes]:


> yes

Enter the interface name [ ]:


> ForcontuConsoleFooInterface

Do you want to load services from the container (yes/no) [no]:


> yes

Type the service name or use keyup or keydown.


This is optional, press enter to continue

Enter your service [ ]:


> database
Enter your service [ ]:
> current_user
Enter your service [ ]:
>

Enter the path for the services


[/modules/custom/forcontu_console/src/]:
>

Do you confirm generation? (yes/no) [yes]:


> yes

// cache:rebuild

156 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

Rebuilding cache(s), wait a moment please.

[OK] Done clearing cache(s).

Generated or updated files

1 - modules/custom/forcontu_console/forcontu_console.[Link]
2 - modules/custom/forcontu_console/src/[Link]
3 - modules/custom/forcontu_console/src/[Link]

[Link]
console/content/es/commands/[Link]

A continuación, se muestra la clase generada:

<?php

namespace Drupal\forcontu_console;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\Core\Session\AccountProxy;

/**
* Class ForcontuConsoleFoo.
*
* @package Drupal\forcontu_console
*/
class ForcontuConsoleFoo implements ForcontuConsoleFooInterface {

/**
* Drupal\Core\Database\Driver\mysql\Connection definition.
*
* @var \Drupal\Core\Database\Driver\mysql\Connection
*/
protected $database;
/**
* Drupal\Core\Session\AccountProxy definition.
*
* @var \Drupal\Core\Session\AccountProxy
*/
protected $currentUser;
/**
* Constructor.
*/
public function __construct(Connection $database, AccountProxy
$current_user) {
$this->database = $database;
$this->currentUser = $current_user;
}
}

La interfaz generada:

<?php

namespace Drupal\forcontu_console;

/**
* Interface ForcontuConsoleFooInterface.
*
* @package Drupal\forcontu_console
*/
interface ForcontuConsoleFooInterface {

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 157
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 29. Plugins II: Tipos de plugins y servicios

También se ha generado el archivo de servicios: forcontu_console.[Link]:

services:
forcontu_console.foo:
class: Drupal\forcontu_console\ForcontuConsoleFoo
arguments: ['@database', '@current_user']

158 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Entidades I:
30
Entidades de configuración
En Drupal 8 encontramos dos variantes de entidades, las entidades
Comparativa D8/D7
de configuración y las entidades de contenido.
Las entidades fueron introducidas en el
núcleo de Drupal 7, aunque la mayor parte
Ya estudiamos la configuración simple, que permite almacenar de la funcionalidad se implementó en el
variables únicas, no repetitivas. En esta unidad estudiaremos las
módulo contribuido Entity API. En Drupal 8
entidades de configuración, que permiten almacenar estructuras de
casi toda esta funcionalidad se ha incluido
configuración más complejas, que se pueden listar, editar, eliminar,
en el núcleo y se han añadido también
etc., pero, sobre todo, se pueden instanciar múltiples veces.
muchas mejoras.

Algunos ejemplos de entidades de configuración son los formatos de


Una diferencia fundamental es que en
fecha, los vocabularios, los estilos de imagen, los roles, los menús,
las vistas, los patrones de URL de Pathauto y los formatos de texto. Drupal 8 para cada entidad se define una
Todos tienen en común que se pueden crear múltiples elementos de clase específica, que se usará para crear
ese tipo, y realizar otras operaciones comunes como editar y instancias de esa entidad.
eliminar. También tienen en común que son entidades que solo se
gestionan desde la configuración o administración del sitio, sin En Drupal 8 se han introducido las
exponerlas al usuario final. entidades de configuración como parte de
la API de configuración.
Las entidades de configuración son traducibles y pueden facilitar
valores por defecto que se tendrán en cuenta durante la instalación.
Las entidades de configuración no pueden tener campos.

Contenidos de la Unidad

30
30.1 Introducción a Entity API y tipos de entidades
30.2 Operaciones sobre entidades
30.3 Entidades de configuración (Configuration Entities)
30.4 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 161
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Introducción a Entity API y tipos de


30.1
entidades
Las entidades fueron introducidas en el núcleo de Drupal 7, aunque la mayor parte
de la funcionalidad se implementó en el módulo contribuido Entity API. En Drupal
8 casi toda esta funcionalidad se ha incluido en el núcleo y se han añadido también
muchas mejoras.

Las entidades son elementos sobre los que podemos realizar operaciones comunes
como crear, cargar, guardar, modificar, eliminar, etc.

Entidades de configuración y entidades de contenido

En Drupal 8 encontramos dos variantes de entidades:

- Entidad de configuración (Configuration Entity). Se utiliza para


crear elementos de configuración. Ya estudiamos la configuración simple,
que permite almacenar variables únicas, no repetitivas. Las entidades
permiten almacenar estructuras de configuración más complejas, que se
pueden listar, editar, eliminar, etc., pero, sobre todo, se pueden instanciar
múltiples veces.

Algunos ejemplos de entidades de configuración son los formatos


de fecha, los vocabularios, los estilos de imagen, los roles, los
menús, las vistas, los patrones de URL de Pathauto y los formatos
de texto. Todos tienen en común que se pueden crear múltiples
elementos de ese tipo, y realizar otras operaciones comunes como editar
y eliminar. También tienen en común que son entidades que solo se
gestionan desde la configuración o administración del sitio, sin exponerlas
al usuario final.

Las entidades de configuración son traducibles y pueden facilitar valores


por defecto que se tendrán en cuenta durante la instalación. Las entidades
de configuración no pueden tener campos.

- Entidad de contenido (Content Entity). Las entidades de contenido


se utilizan para crear elementos de contenido. Se diferencian
principalmente porque tienen campos. Las opciones de Gestionar campos,
Gestionar presentación, etc., son comunes en las entidades de contenido.
También son traducibles y, opcionalmente, pueden tener revisiones.

Algunos ejemplos de entidades de contenido son los nodos, los


comentarios, los términos de taxonomía, los usuarios, los archivos, los
tipos de bloques personalizados, etc.

Una diferencia fundamental entre ambas variantes, es que las entidades de


configuración utilizan el sistema de configuración para almacenar la
información, en lugar de la base de datos, que es el almacenaje utilizado por las
entidades de contenido. El funcionamiento es similar al estudiado para la
Configuración Simple, así que la configuración parte de archivos YAML, pero luego

162 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

se gestiona desde la base de datos. A la hora de exportar/importar esta


configuración entre entornos, se generan archivos YAML, lo que facilita también el
control de versiones.

Tipos de entidad

Con tipo de entidad nos referimos a la clase que permite crear o instanciar
entidades de ese tipo. Por ejemplo, el tipo de entidad Node permite crear nodos,
que son las entidades de ese tipo.

Las clases que definen entidades se crean en el directorio /src/Entity del módulo,
correspondiéndole el espacio de nombres \Drupal\[módulo]\Entity. Dentro la clase
se añade, vía annotations (etiqueta @EntityType), información de configuración de
la clase (etiqueta, controladores, tablas, etc.). Este es el método que utiliza el
sistema para descubrir qué tipos de entidad implementa cada módulo.

El nombre de sistema de un tipo de entidad debe incluir como prefijo el nombre


del módulo que la implementa, siempre que no sean iguales. El nombre de la clase
no requiere el prefijo, ya que está definida en el espacio de nombres del módulo.
Por ejemplo, el tipo de entidad 'taxonomy_term', se define en el módulo Taxonomy
a través de la clase Drupal\taxonomy\Entity\Term.

Tipos de entidades (ver diagrama):


[Link]

Bundles

Un bundle (que podríamos traducir como paquete) es un subtipo dentro de un tipo


de entidad. Veamos algunos ejemplos que podemos encontrar en el núcleo:

- Los tipos de contenido (Artículo, Página básica, etc.) son bundles del
tipo de entidad Node.
- Los tipos de comentario son bundles del tipo de entidad Comment.
- Los vocabularios son bundles de la entidad Taxonomy.
- Los tipos de bloques personalizados son bundles de la entidad Blocks.

No todas las entidades tienen que tener bundles. Por ejemplo, la entidad User no
tiene bundles. Las entidades de configuración generalmente no tienen bundles (Por
ejemplo, Role, Menu, View, etc.).

Módulo Forcontu Entities

En esta unidad crearemos el módulo Forcontu Entities (forcontu_entities), donde


iremos incorporando los ejemplos que detallaremos en los próximos apartados.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 163
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

30.2 Operaciones sobre entidades

En este apartado estudiamos las operaciones y métodos que se pueden aplicar a


un objeto de tipo entidad (tanto de configuración como de contenido). Además de
las operaciones CRUD (crear, leer, actualizar y eliminar) sobre entidades, también
veremos los hooks que nos permiten actuar en diferentes eventos relacionados.

Puedes leer más sobre estas operaciones en:

Entity CRUD, editing and view hooks:


[Link]
entity_crud/8

Working with the Entity API


[Link]

Comprobación de entidades

El operador de PHP instanceof nos permite verificar si una variable es un objeto


de tipo entidad. Solo tenemos que localizar la interface correspondiente (junto a
su espacio de nombres):

// Comprueba si es una entidad


if ($entity instanceof \Drupal\Core\Entity\EntityInterface) {
}

// Comprueba si es una entidad de contenido


if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface)
{
}

// Comprueba si es una entidad de configuración


if ($entity instanceof
\Drupal\Core\Config\Entity\ConfigEntityInterface) {
}

// Comprueba si es una entidad de tipo Node


if ($entity instanceof \Drupal\node\NodeInterface) {
}

Una vez hemos comprobado que el objeto es una entidad, podemos obtener el tipo
de entidad con:

// Obtiene el tipo de entidad


$entity->getEntityTypeId();

164 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Obtener información de entidades

En EntityInterface se definen los métodos comunes a todas las entidades:

[Link]
nterface/EntityInterface/8

// Obtiene el ID de la entidad
$entity->id();

// Otiene el UUID (identificador único) de la entidad


$entity->uuid();

// Obtiene el bundle (subtipo) de la entidad


$entity->bundle();

// Comprueba si la entidad es nueva (se está creando)


$entity->isNew();

// Obtiene la etiqueta de la entidad.


$entity->label();

// Obtiene un array con todas las propiedades de la entidad


$entity->toArray();

// Obtiene el objeto URL que apunta a la entidad


$entity->toUrl();

// Obtiene el enlace HTML que apunta a la entidad


$entity->toLink();

EntityTypeManager y almacén de entidades

La clase EntityTypeManager, a través del servicio entity_type.manager, nos


permite gestionar cualquier tipo de entidad.

Servicio: entity_type.manager
Clase:
[Link]
hp/class/EntityTypeManager/8

Esta clase es fundamental para cargar entidades, accediendo primero al almacén


(storage) de un tipo de entidad determinado:

$node_storage = \Drupal::entityTypeManager()->getStorage('node');
$user_storage = \Drupal::entityTypeManager()->getStorage('user');

Como en otros muchos casos, podemos utilizar el servicio desde la clase global
\Drupal, como en el ejemplo anterior, o inyectando el servicio en la clase en que
estemos trabajando.

El almacén es un objeto que implementa EntityStorageInterface, añadiendo una


serie de métodos para realizar operaciones CRUD sobre entidades:

[Link]
[Link]/interface/EntityStorageInterface/8

Nota: En ningun caso hay que usar EntityManager, que ha quedado


obsoleto frente a EntityTypeManager.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 165
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Carga de entidades

- load($id). Carga una entidad identificada por su ID.

$node = $node_storage->load(1);

- loadMultiple(array $ids). Carga varias entidades según los IDs


especificados en el array $ids. Devuelve un array de entidades, indexadas
por su ID. Si no se pasa un array como parámetro (NULL), se cargan todas
las entidades.

$selected_users = [4, 13, 27];


$accounts = $user_storage->loadMultiple($selected_users);

- loadByProperties(array $values). Carga varias entidades filtrando por


las propiedades especificadas en el array $values.

$accounts = $user_storage->loadByProperties(['name' => $username,


'status' => 1]);

Durante la carga de entidades podemos utilizar el siguiente hook:

- hook_ENTITY_TYPE_load($entities). El parámetro pasado es un array


con las entidades cargadas, lo que nos permitirá realizar operaciones
sobre un conjunto de entidades, generalmente cargado con el método
loadMultiple().

[Link]
n/hook_ENTITY_TYPE_load/8

Crear y actualizar entidades

- create(array $values). Crea un objeto de entidad, sin guardarlo. En este


ejemplo se crea un nuevo nodo, al que añadiremos propiedades en el
array $values.

$node = $node_storage->create(['type' => 'article',


'title' => 'Sample node']);

- save($entity). Guarda permanentemente la entidad.

$new_user = $user_storage->create([
'name' => 'test_user',
'mail' => 'foo@[Link]',
'pass' => '123456',
]);

$new_user->save();

En este ejemplo se crea un usuario y se guarda permanentemente. Hemos


usado el método save() de la entidad en lugar del método save($entity)

166 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

del almacén.

También existe un método create() en la clase Entity, que funcionaría


exactamente igual (\Drupal\Core\Entity::create()).

Cuando se guarda una entidad con save(), el sistema identifica si se trata de una
nueva entidad (aún no tiene un ID asignado) o de una entidad existente. En
algunos casos, como cuando importamos elementos, puede ser necesario forzar a
que la entidad se cree. Esto lo podemos hacer con el método enforceIsNew(),
siempre teniendo en cuenta que el ID asignado a la entidad no puede existir en la
base de datos.

Cuando son otros módulos los que crean las entidades, podemos intervenir durante
la creación implementando el hook:

- hook_ENTITY_TYPE_create($entity)

Por ejemplo, en nuestro módulo Forcontu Entities implementaríamos la función


forcontu_entities_node_create($entity), para intervenir tras la creación de
entidades de tipo nodo:

/**
* Implements ENTITY_TYPE_create() for node.
*/
function forcontu_entities_node_create(\Drupal\Core\Entity\EntityInterface $entity) {

\Drupal::logger('forcontu_entities')->info('Node created: @label',


['@label' => $entity->label()]);
}

[Link]
n/hook_ENTITY_TYPE_create/8

También podemos realizar acciones en estos otros puntos:

- hook_ENTITY_TYPE_presave($entity). Actúa antes de que la entidad


sea creada o actualizada.

[Link]
n/hook_ENTITY_TYPE_presave/8

- hook_ENTITY_TYPE_insert($entity). Actúa cuando se crea una nueva


entidad. Se ejecuta una vez que la nueva entidad ha sido almacenada. En
este punto, por tanto, no podemos modificar la entidad.

[Link]
n/hook_ENTITY_TYPE_insert/8

- hook_ENTITY_TYPE_update($entity). Actúa cuando se actualiza una


entidad existente. Se ejecuta una vez que la entidad se ha almacenado.
En este punto, por tanto, no podemos modificar la entidad.

[Link]
n/hook_ENTITY_TYPE_update/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 167
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Eliminar entidades

Podemos eliminar entidades de dos formas:

- Método delete(array $entities) en el almacén de la entidad. Elimina


permanentemente las entidades referenciadas en el array $entities. Esta
es la solución adecuada cuando tenemos que eliminar varias entidades a
la vez.

$node_storage->delete([$id1 => $entity1, $id2 => $entity2]);

- Método delete() de la entidad a eliminar.

$entity->delete();

Cuando se elimina una entidad de otro módulo, podemos utilizar estos hooks:

- hook_ENTITY_TYPE_predelete($entity). Actúa antes de que se


elimine la entidad.

[Link]
on/hook_ENTITY_TYPE_predelete/8

- hook_ENTITY_TYPE_delete($entity). Actúa después de que la entidad


haya sido eliminada. Se utiliza generalmente para eliminar información
relacionada con la entidad que hayamos podido almacenar en otras tablas
o variables de configuración desde nuestro módulo.

[Link]
on/hook_ENTITY_TYPE_delete/8

Control de acceso a entidades

Para comprobar si un usuario puede realizar operaciones sobre una entidad ya


cargada, usaremos el método access() de la entidad.

[Link]
ntity::access/8

Entity::access($operation, AccountInterface $account = NULL,


$return_as_object = FALSE)

- $operation. Indicamos la operación a evaluar: 'create', 'view', 'edit',


'delete'.
- $account. Si no se indica una cuenta de usuario, se evaluará sobre el
usuario actual.

if ($entity->access('view')) {
}

168 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Cuando no hay una entidad disponible, pero necesitamos conocer si el usuario


tiene acceso a ese tipo de entidad, tendremos que instanciar un manejador de
control de acceso (desde el entityTypeManager):

[Link]
[Link]/interface/EntityAccessControlHandlerInterface/8

$access_control_handler =
\Drupal::entityTypeManager()->getAccessControlHandler('node');

if (($account->hasPermission('add content to books') &&


$access_control_handler->createAccess('book'){
}

En el ejemplo, el método createAccess() comprueba si el usuario actual tiene


permisos para crear nodos de tipo book (nombre de sistema del bundle o tipo de
contenido).

Acceso a valores de campos

Para acceder al valor de un campo en una entidad podemos utilizar este método:

$entity->get("FIELD")->getValue();

Independientemente de si el campo acepta un único valor o varios, el valor


devuelto es siempre un array. Además, si el campo tiene varios atributos,
también serán devueltos, en forma de array asociativo. Veamos un ejemplo:

$body = $node->get('body')->getValue();
dpm($body);

El valor devuelto sería el mostrado en la Figura [F30.2a]:

F30.2a
Acceso a campos
Valor del campo devuelto
en formato array.

Para acceder al valor del campo (o de cualquier otro elemento del campo):

// valor del campo


$body[0]['value'];

// resumen
$body[0]['summary'];

// formato
$body[0]['format'];

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 169
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

En el siguiente ejemplo accedemos a los valores almacenados en el campo


field_tags (campo Etiquetas), que es un campo de tipo referencia a Términos de
taxonomía.

Al tratarse de un campo de referencia a otra entidad, lo que se almacena es


únicamente el ID de cada entidad. Además, en este caso el campo puede tener
asociados varios valores, por lo que tenemos que recorrer el array y cargar cada
elemento. Al tratarse de términos de taxonomía hemos cargado el almacén del tipo
de entidad 'taxonomy_term'.

$terms = $node->get('field_tags')->getValue();
$term_storage =
\Drupal::entityTypeManager()->getStorage('taxonomy_term');

foreach($terms as $term_id) {
$term = $term_storage->load($term_id['target_id']);
dpm($term->label());
}

Más adelante veremos métodos específicos de los vocabularios y términos de


taxonomía.

Acceso a entidades con entityQuery

El servicio [Link], gestionado por la clase entityQuery, permite realizar


consultas sobre entidades con un formato similar al estudiado para las consultas a
la base de datos.

[Link]

Los métodos disponibles para configurar la consulta pueden consultarse en la


interfaz:

[Link]
[Link]/interface/QueryInterface/8

En el siguiente ejemplo se obtienen los 5 últimos nodos de tipo 'article', según la


fecha de modificación.

$query = \Drupal::entityQuery('node')
->condition('status', 1)
->condition('type', 'article')
->range(0,5)
->sort('changed', 'DESC');

$entity_ids = $query->execute();

Nótese que el objetivo de entityQuery es obtener un listado de IDs de entidades


que cumplan con las condiciones establecidas. Para cargar las entidades
resultantes, utilizaremos los método load() o loadMultiple() del almacén de ese tipo
de entidad, como hemos visto anteriormente.

En el ejemplo hemos utilizado la comprobación de 'type', ya que se trata de


entidades de tipo Node, donde type equivale al bundle o tipo de contenido. En
entidades genéricas, usaremos 'bundle' en la condición.

170 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Entidades de configuración
30.3
(Configuration entities)
Aunque en este apartado se explica paso a paso cómo crear un nuevo tipo de
entidad de configuración, para una implementación más ágil y para evitar errores,
se recomienda usar Drupal Console para generar el esqueleto de la entidad.
Veremos en el último apartado los comandos disponibles.

Como ejemplo, vamos a crear el tipo de entidad de configuración Section


(Sección), con ID 'forcontu_entities_section', de forma que podremos crear
secciones en el sitio (Noticias, Blog, etc.).

Cada Sección tendrá las siguientes propiedades:

- Un título de sección. Este título se mostrará por encima del título de


página, siempre que la página actual pertenezca a alguna sección.

- Un nombre de sistema o identificador.

- Un patrón de URL. Este patrón determinará qué URLs pertenecen a la


sección. Una URL solo puede pertenecer a una sección. La comprobación
de a qué sección pertenece una URL, se realizará por orden, comprobando
cada sección, hasta que se encuentre una que concuerde.

- Un color. Este parámetro permitirá hacer cambios de estilos en la


cabecera de la página o sección.

Vamos a dividir la creación de una entidad de configuración en cuatro partes, que


no tienen porqué implementarse en este orden:

Paso 1. Clases del tipo de entidad

En este primer paso vamos a crear la interfaz y la clase que implementa la entidad.

La interfaz extiende a ConfigEntityInterface. En ella declaramos los métodos get y


set de las propiedades adicionales de la entidad. No olvides comentar
adecuadamente estos métodos. Generalmente añadiremos los comentarios en la
interfaz, y los heredaremos desde la clase que la implementa.

src/Entity/[Link]

<?php

namespace Drupal\forcontu_entities\Entity;

use Drupal\Core\Config\Entity\ConfigEntityInterface;

/**
* Provides an interface for defining Section entities.
*/
interface SectionInterface extends ConfigEntityInterface {

/**
* Returns the URL pattern of the section.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 171
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

*
* @return string
* The URL pattern of the section
*/
public function getUrlPattern();

/**
* Returns the color of the section.
*
* @return string
* Color in HEX format
*/
public function getColor();

/**
* Sets the section URL pattern.
*
* @param string $pattern
* The pattern URL
*
* @return $this
*/
public function setUrlPattern($pattern);

/**
* Sets the section Color.
*
* @param string $color
* Color in HEX format
*
* @return $this
*/
public function setColor($color);

A continuación, implementamos la clase Section, que extiende a ConfigEntityBase


e implementa la interfaz anterior. Recuerda que, aunque el nombre de sistema o
id del tipo de entidad incluye como prefijo el nombre del módulo, la clase no lo
requiere.

La clase tiene una cabecera de comentario con formato annotation, que define las
propiedades de la entidad dentro de la directiva @ConfigEntityType.

src/Entity/[Link] (Annotation)

<?php

namespace Drupal\forcontu_entities\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;

/**
* Defines the Section entity.
*
* @ConfigEntityType(
* id = "forcontu_entities_section",
* label = @Translation("Section"),
* handlers = {
* "list_builder" = "Drupal\forcontu_entities\SectionListBuilder",
* "form" = {
* "add" = "Drupal\forcontu_entities\Form\SectionForm",
* "edit" = "Drupal\forcontu_entities\Form\SectionForm",
* "delete" = "Drupal\forcontu_entities\Form\SectionDeleteForm"
* },
* "route_provider" = {
* "html" = "Drupal\forcontu_entities\SectionHtmlRouteProvider",
* },
* },
* config_prefix = "forcontu_entities_section",
* admin_permission = "administer site configuration",

172 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/admin/structure/forcontu_entities_section/{forcontu_entities_section}",
* "add-form" = "/admin/structure/forcontu_entities_section/add",
* "edit-form" = "/admin/structure/forcontu_entities_section/{forcontu_entities_section}/edit",
* "delete-form" = "/admin/structure/forcontu_entities_section/{forcontu_entities_section}/delete",
* "collection" = "/admin/structure/forcontu_entities_section"
* }
* )
*/

Dentro de @ConfigEntityType definimos las siguientes propiedades:

- id. Nombre de sistema del tipo de entidad, que debe llevar como prefijo
el nombre del módulo (salvo que el módulo y la entidad tengan el mismo
nombre).

- label. Nombre de la entidad. Es el nombre con el que identificamos a la


entidad en el área de administración (sin el nombre del módulo).

- handlers. Aquí indicamos las clases que se encargarán de determinadas


funcionalidades.
o 'list_builder'. Clase que genera el listado de entidades.
o 'form'. Clases de formulario (add, edit, delete).
o 'route_provider'. Como veremos en el próximo apartado, se trata
de una clase para generar las rutas de gestión de la entidad de
forma dinámica.

- config_prefix. Hace referencia al prefijo que tendrán los nombres de


configuración que se creen para almacenar la información.

- admin_permission. Permiso de administración para acceder a la página


de gestión de entidades de tipo Section. Como ya hemos estudiado,
podemos definir un permiso específico para nuestro módulo.

Generalmente solo es necesario un permiso para dar acceso total a la


gestión de un tipo de entidad de configuración. Si se requiere una lógica
más compleja, se puede especificar un método controlador personalizado.

- entity_keys. Incluye los nombres de los campos de identificación de la


entidad (id, label y uuid).

- links. Enlaces de gestión de la entidad:


o 'canonical'. Ver la entidad.
o 'add-form'. Crear nueva entidad.
o 'edit-form'. Editar entidad existente.
o 'delete-form'. Eliminar entidad.
o 'colletion'. Página principal de gestión de las entidades de este
tipo.

En el mismo archivo definimos la clase Section, indicando sus propiedades y los


métodos exigidos por la interfaz.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 173
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

src/Entity/[Link] (class)

class Section extends ConfigEntityBase implements SectionInterface {

/**
* The Section ID.
*
* @var string
*/
protected $id;

/**
* The Section label.
*
* @var string
*/
protected $label;

/**
* URL pattern.
*
* @var string
*/
protected $urlPattern;

/**
* Color (HEX format)
*
* @var string
*/
protected $color;

/**
* {@inheritdoc}
*/
public function getUrlPattern() {
return $this->urlPattern;
}

/**
* {@inheritdoc}
*/
public function getColor() {
return $this->color;
}

/**
* {@inheritdoc}
*/
public function setUrlPattern($pattern) {
$this->urlPattern = $pattern;
return $this;
}

/**
* {@inheritdoc}
*/
public function setColor($color) {
$this->color = $color;
return $this;
}
}

Nota: en los métodos set devolvemos el objeto $this para permitir el


encadenamiento de métodos:

$entity->setUrlPattern($pattern)->setColor($color);

174 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Paso 2. Definición de rutas y enlaces de menú

Los enlaces que necesitamos para gestionar las entidades, pueden definirse de dos
formas:

Método 1 (rutas estáticas)

Es el método clásico que ya hemos estudiado, definiendo las rutas en el


archivo .[Link].

entity.forcontu_entities_section.collection:
path: '/admin/structure/forcontu_entities_section'
defaults:
_entity_list: 'forcontu_entities_section'
_title: 'Sections configuration'
requirements:
_permission: 'administer site configuration'

entity.forcontu_entities_section.add_form:
path: '/admin/structure/forcontu_entities_section/add'
defaults:
_entity_form: 'forcontu_entities_section.add'
_title: 'Add section'
requirements:
_permission: 'administer site configuration'

entity.forcontu_entities_section.edit_form:
path: '/admin/structure/forcontu_entities_section/{example}'
defaults:
_entity_form: 'forcontu_entities_section.edit'
_title: 'Edit section'
requirements:
_permission: 'administer site configuration'

entity.forcontu_entities_section.delete_form:
path:
'/admin/structure/forcontu_entities_section/{example}/delete'
defaults:
_entity_form: 'forcontu_entities_section.delete'
_title: 'Delete section'
requirements:
_permission: 'administer site configuration'

Método 2 (rutas dinámicas)

Genera las rutas de forma dinámica a partir de una clase proveedora de rutas. Este
es el método que hemos definido al declarar la clase Section, a través del
parámetro "route_provider":

* "route_provider" = {
* "html" = "Drupal\forcontu_entities\SectionHtmlRouteProvider",
* },

Se trata de un código genérico que podemos reutilizar para otros tipos de entidad,
creando siempre una clase específica para cada tipo. Además, este es el método
que implementa Drupal Console.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 175
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

src/[Link]

<?php

namespace Drupal\forcontu_entities;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
use Symfony\Component\Routing\Route;

/**
* Provides routes for Section entities.
*
* @see Drupal\Core\Entity\Routing\AdminHtmlRouteProvider
* @see Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider
*/
class SectionHtmlRouteProvider extends AdminHtmlRouteProvider {

/**
* {@inheritdoc}
*/
public function getRoutes(EntityTypeInterface $entity_type) {
$collection = parent::getRoutes($entity_type);

$entity_type_id = $entity_type->id();

if ($collection_route = $this->getCollectionRoute($entity_type)) {
$collection->add("entity.{$entity_type_id}.collection",
$collection_route);
}
return $collection;
}

/**
* Gets the collection route.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return \Symfony\Component\Routing\Route|null
* The generated route, if available.
*/
protected function getCollectionRoute(EntityTypeInterface $entity_type) {
if ($entity_type->hasLinkTemplate('collection') &&
$entity_type->hasListBuilderClass()) {
$entity_type_id = $entity_type->id();
$route = new Route($entity_type->getLinkTemplate('collection'));
$route
->setDefaults([
'_entity_list' => $entity_type_id,
// Make sure this is not a TranslatableMarkup object as the
// TitleResolver translates this string again.
'_title' => (string) $entity_type->getLabel(),
])
->setRequirement('_permission', $entity_type->getAdminPermission())
->setOption('_admin_route', TRUE);

return $route;
}
}
}

Independientemente del método utilizado para declarar las rutas, tendremos que
crear el enlace de menú a la página de administración de esta entidad.

forcontu_entities.[Link]

# Section menu items definition


entity.forcontu_entities_section.collection:
title: 'Section'
route_name: entity.forcontu_entities_section.collection
description: 'List Section (bundles)'
parent: system.admin_structure
weight: 99

176 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

También definiremos un enlace de acción para añadir nuevas entidades de tipo


Section.

forcontu_entities.[Link]

entity.forcontu_entities_section.add_form:
route_name: 'entity.forcontu_entities_section.add_form'
title: 'Add Section'
appears_on:
- entity.forcontu_entities_section.collection

Paso 3. Esquema de configuración

El archivo de esquema de configuración incluye las propiedades definidas en la


clase Section:

config/schema/forcontu_entities_section.[Link]

forcontu_entities.forcontu_entities_section.*:
type: config_entity
label: 'Section config'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
uuid:
type: string
urlPattern:
type: string
label: 'URL Pattern'
color:
type: string
label: 'Color'

Paso 4. Clases controladoras de la entidad

Ya solo nos queda definir las páginas que permiten gestionar las entidades de tipo
Section. Volvemos a la sección 'handlers' de la definición en annotation de la
entidad, para ver las clases que debemos crear.

Comenzamos con la página donde se listan, en forma de tabla, las entidades


creadas. En el método buildHeader() definimos los encabezados de la tabla, y en
buildRow() construimos cada fila, indicando los valores de cada columna. Los
nombres de los elementos de formulario deben coincidir con los nombres de las
propiedades de la entidad.

src/[Link]

<?php

namespace Drupal\forcontu_entities;

use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 177
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

/**
* Provides a listing of Section entities.
*/
class SectionListBuilder extends ConfigEntityListBuilder {

/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['label'] = $this->t('Section');
$header['id'] = $this->t('Machine name');
$header['urlPattern'] = $this->t('URL pattern');
$header['color'] = $this->t('Color');
return $header + parent::buildHeader();
}

/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['label'] = $entity->label();
$row['id'] = $entity->id();
$row['urlPattern'] = $entity->getUrlPattern();
$row['color'] = $entity->getColor();

return $row + parent::buildRow($entity);


}
}

El formulario SectionForm servirá para crear y editar entidades de tipo Section.


Añadiremos al formulario las propiedades personalizadas de nuestro tipo de
entidad. También podemos añadir el método validateForm() para incluir
validaciones de campos.

src/Form/[Link]

<?php

namespace Drupal\forcontu_entities\Form;

use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;

/**
* Class SectionForm.
*
* @package Drupal\forcontu_entities\Form
*/
class SectionForm extends EntityForm {

/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);

$forcontu_entities_section = $this->entity;
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $forcontu_entities_section->label(),
'#description' => $this->t("Label for the Section."),
'#required' => TRUE,
];

$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $forcontu_entities_section->id(),
'#machine_name' => [
'exists' => '\Drupal\forcontu_entities\Entity\Section::load',
],

178 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

'#disabled' => !$forcontu_entities_section->isNew(),


];

$form['urlPattern'] = [
'#type' => 'textfield',
'#title' => $this->t('URL pattern'),
'#maxlength' => 255,
'#default_value' => $forcontu_entities_section->getUrlPattern(),
'#description' => $this->t("URL pattern for the Section."),
'#required' => TRUE,
];

$form['color'] = [
'#type' => 'color',
'#title' => $this->t('Color'),
'#default_value' => $forcontu_entities_section->getColor(),
'#description' => $this->t("Color for the Section."),
'#required' => TRUE,
];

return $form;
}

/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$forcontu_entities_section = $this->entity;
$status = $forcontu_entities_section->save();

switch ($status) {
case SAVED_NEW:
drupal_set_message($this->t('Created the %label Section.', [
'%label' => $forcontu_entities_section->label(),
]));
break;

default:
drupal_set_message($this->t('Saved the %label Section.', [
'%label' => $forcontu_entities_section->label(),
]));
}
$form_state->setRedirectUrl($forcontu_entities_section-
>toUrl('collection'));
}
}

Por último, implementamos el formulario para eliminar la entidad.

src/Form/[Link]

<?php

namespace Drupal\forcontu_entities\Form;

use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;

/**
* Builds the form to delete Section entities.
*/
class SectionDeleteForm extends EntityConfirmFormBase {

/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete %name?', ['%name'
=> $this->entity->label()]);
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 179
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.forcontu_entities_section.collection');
}

/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->entity->delete();

drupal_set_message(
$this->t('content @type: deleted @label.',
[ '@type' => $this->entity->bundle(),
'@label' => $this->entity->label(),
])
);
$form_state->setRedirectUrl($this->getCancelUrl());
}
}

Ya tenemos toda la funcionalidad implementada. Tras instalar el módulo (o vaciar


caché), tendremos acceso a la gestión de Secciones desde [F30.3a]:

Administración  Estructura  Section

F30.3a
Gestión de entidades
Section
Listado, inicialmente vacío,
se secciones.

Haciendo clic en Add Section, podemos crear una nueva entidad Section [F30.3b]:

F30.3b
Gestión de entidades
Section
Crear (o editar) una
entidad Section.

En la Figura [F30.3c] se muestra el listado de secciones creadas.

180 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

F30.3c
Gestión de entidades
Section
Listado de secciones.

Acceder a las entidades creadas

Para acceder a las entidades creadas, podemos utilizar cualquiera de los métodos
estudiados previamente. Por ejemplo, a través de entityQuery:

$query = \Drupal::entityQuery('forcontu_entities_section');
$sections = $query->execute();

$section_storage =
\Drupal::entityTypeManager()->getStorage('forcontu_entities_section');

foreach($sections as $section_id) {
$section = $section_storage->load($section_id);
};

Enlaces relacionados:

Creating a configuration entity type in Drupal 8


[Link]
entity-type-in-drupal-8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 181
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

30.4 Hazlo desde la consola

Crear una entidad de configuración (Drupal Console)

drupal generate:entity:config

Genera una nueva entidad de configuración.

drupal generate:entity:config options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:entity:config
Enter the module name [admin_toolbar]:
> forcontu_console

Enter the class of your new config entity [DefaultEntity]:


> FooEntity

Enter the name of your new config entity [foo_entity]:


>

Enter the label of your new config entity [Foo entity]:


>

Enter the base-path for the config entity routes


[/admin/structure]:
>

Generated or updated files

1 - modules/custom/forcontu_console/config/schema/foo_entity.[Link]
2 - modules/custom/forcontu_console/forcontu_console.[Link]
3 - modules/custom/forcontu_console/forcontu_console.[Link]
4 - modules/custom/forcontu_console/src/Entity/[Link]
5 - modules/custom/forcontu_console/src/Entity/[Link]
6 - modules/custom/forcontu_console/src/[Link]
7 - modules/custom/forcontu_console/src/Form/[Link]
8 - modules/custom/forcontu_console/src/Form/[Link]
9 - modules/custom/forcontu_console/src/[Link]

[Link]
console/content/es/commands/[Link]

Una vez generado el tipo de entidad, el primer archivo que tenemos que analizar
es la declaración de la entidad, y concretamente la parte de annotations
(@ConfigEntityType). Aquí encontraremos las clases y rutas relacionadas con la
entidad creada.

182 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

Archivo: src/Entity/[Link]
<?php

namespace Drupal\forcontu_console\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;

/**
* Defines the Foo entity entity.
*
* @ConfigEntityType(
* id = "foo_entity",
* label = @Translation("Foo entity"),
* handlers = {
* "list_builder" = "Drupal\forcontu_console\FooEntityListBuilder",
* "form" = {
* "add" = "Drupal\forcontu_console\Form\FooEntityForm",
* "edit" = "Drupal\forcontu_console\Form\FooEntityForm",
* "delete" = "Drupal\forcontu_console\Form\FooEntityDeleteForm"
* },
* "route_provider" = {
* "html" = "Drupal\forcontu_console\FooEntityHtmlRouteProvider",
* },
* },
* config_prefix = "foo_entity",
* admin_permission = "administer site configuration",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/admin/structure/foo_entity/{foo_entity}",
* "add-form" = "/admin/structure/foo_entity/add",
* "edit-form" = "/admin/structure/foo_entity/{foo_entity}/edit",
* "delete-form" = "/admin/structure/foo_entity/{foo_entity}/delete",
* "collection" = "/admin/structure/foo_entity"
* }
* )
*/
class FooEntity extends ConfigEntityBase implements FooEntityInterface {

/**
* The Foo entity ID.
*
* @var string
*/
protected $id;

/**
* The Foo entity label.
*
* @var string
*/
protected $label;

Recuerda que solo estamos generando un esqueleto, que debemos completar. Los
cambios mínimos a realizar son:

- src/Entity/[Link]. Añadir a la interfaz los métodos


get y set con las propiedades específicas de nuestra entidad.

- src/Entity/[Link]. Añadir a la clase principal de la entidad, las


propiedades específicas y los métodos get y set descritos en la interfaz.

- src/Form/FooEntityForm. Añadir los elementos de formulario


adicionales, para poder almacenar las propiedades específicas de la
entidad. Los nombres de los elementos de formulario tienen que coincidir
con los nombres de las propiedades.
- config/schema/foo_entity.[Link]. Añadir las propiedades

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 183
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 30. Entidades I: Entidades de configuración

específicas de la entidad.

- src/[Link]. En el listado de entidades, debemos


modificar los campos a mostrar, tanto en el encabezado (buildHeader())
como en las filas (buildRow()).

Una vez realizados estos cambios, instala el módulo (o actualiza la caché), y accede
a la URL de administración del tipo de entidad:

/admin/structure/foo_entity

184 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Entidades II:
31
Entidades de contenido
Las entidades de contenido se utilizan para crear elementos de
Comparativa D8/D7
contenido. Una de las principales diferencias con respecto a las
entidades de configuración es que tienen campos. Las opciones de Las entidades fueron introducidas en el
Gestionar campos, Gestionar presentación, etc., son comunes en las núcleo de Drupal 7, aunque la mayor parte
entidades de contenido. También son traducibles y, opcionalmente, de la funcionalidad se implementó en el
pueden tener revisiones. módulo contribuido Entity API. En Drupal 8
casi toda esta funcionalidad se ha incluido
en el núcleo y se han añadido también
Algunos ejemplos de entidades de contenido son los nodos, los
muchas mejoras.
comentarios, los términos de taxonomía, los usuarios, los archivos,
los tipos de bloques personalizados, etc.
Una diferencia fundamental es que en
En esta unidad veremos cómo crear entidades de contenido y Drupal 8 para cada entidad se define una
campos. clase específica, que se usará para crear
instancias de esa entidad.

En Drupal 8 son las entidades de


contenido las que pueden tener campos.

Contenidos de la Unidad
31.1 Entidades de contenido (Content entities)
31.2 Crear un tipo de contenido personalizado

31
31.3 Campos personalizados
31.4 Theming de entidades
31.5 Programación de taxonomías
31.6 Validación de entidades
31.7 Acceso a rutas y operaciones sobre entidades
31.8 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 187
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

31.1 Entidades de contenido (Content entities)

Nuevamente recomendamos el uso de Drupal Console para generar el esqueleto


de los tipos de entidad de contenido. El número de archivos requeridos para definir
una entidad de contenido es superior al necesario para las entidades de
configuración, y es muy fácil cometer errores si intentamos crearlo todo desde
cero.

Como ejemplo, vamos a crear el tipo de entidad de contenido Message (Mensaje),


con ID 'forcontu_entities_message'. Esta entidad permitirá el envío de
mensajes entre usuarios del sitio.

La entidad tendrá bundles, así que se podrán definir diferentes tipos de mensajes.
Para simplificar, la entidad no será traducible y tampoco se podrán crear revisiones
de los mensajes.

Cada Mensaje tendrá las siguientes propiedades:

- Destinatario (user_to). Referencia a un usuario del sitio.

- Asunto (subject). Texto limitado a 100 caracteres.

- Contenido (content). Texto largo.

- Leído (is_read). Campo de tipo Booleano. Acepta los valores FALSE (no
leído) y TRUE (leído). Esta propiedad no será rellenable por el usuario.

Vamos a ver, paso a paso, los archivos que intervienen en la creación de la entidad.
Consulta el último apartado de esta unidad para crear la entidad desde Drupal
Console.

Clases de la entidad Message

En este primer paso vamos a crear la interfaz y la clase que implementa la entidad.

La interfaz extiende a ContentEntityInterface, EntityChangedInterface y


EntityOwnerInterface. En función de otras características, puede extender a alguna
interfaz adicional.

En la interfaz declaramos los métodos get y set de los campos de la entidad. A


diferencia de las entidades de configuración, en las entidades de contenido
definiremos las propiedades adicionales como campos (base fields). Lo
veremos más adelante.

No olvides comentar adecuadamente estos métodos. Generalmente añadiremos los


comentarios en la interfaz, y los heredaremos desde la clase que la implementa.

188 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

src/Entity/[Link]

<?php

namespace Drupal\forcontu_entities\Entity;

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\user\EntityOwnerInterface;

/**
* Provides an interface for defining Message entities.
*
* @ingroup forcontu_entities
*/
interface MessageInterface extends ContentEntityInterface,
EntityChangedInterface, EntityOwnerInterface {

// Add get/set methods for your configuration properties here.

/**
* Gets the Message type.
*
* @return string
* The Message type.
*/
public function getType();

/**
* Gets the Message subject.
*
* @return string
* Subject of the Message.
*/
public function getSubject();

/**
* Sets the Message subject.
*
* @param string $subject
* The Message subject.
*
* @return \Drupal\forcontu_entities\Entity\MessageInterface
* The called Message entity.
*/
public function setSubject($subject);

/**
* Gets the Message creation timestamp.
*
* @return int
* Creation timestamp of the Message.
*/
public function getCreatedTime();

/**
* Sets the Message creation timestamp.
*
* @param int $timestamp
* The Message creation timestamp.
*
* @return \Drupal\forcontu_entities\Entity\MessageInterface
* The called Message entity.
*/
public function setCreatedTime($timestamp);

/**
* Returns the Message published status indicator.
*
* Unpublished Message are only visible to restricted users.
*
* @return bool
* TRUE if the Message is published.
*/
public function isPublished();

/**
* Sets the published status of a Message.
*

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 189
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

* @param bool $published


* TRUE to set this Message to published, FALSE to set it to
unpublished.
*
* @return \Drupal\forcontu_entities\Entity\MessageInterface
* The called Message entity.
*/
public function setPublished($published);

/**
* Gets the To user id.
*
* @return int
* The user id.
*/
public function getUserToId();

/**
* Sets the To user id.
*
* @param int $uid
* To user id.
*
* @return $this
*/
public function setUserToId($uid);

/**
* Gets the To user object.
*
* @return UserInterface
* The user object.
*/
public function getUserTo();

/**
* Sets the To user object.
*
* @param string $account
* The user object.
*
* @return $this
*/
public function setUserTo(UserInterface $account);

/**
* Gets the Content.
*
* @return string
* Message content.
*/
public function getContent();

/**
* Sets the message's content.
*
* @param string $content
* Message's content.
*
* @return $this
*/
public function setContent($content);

/**
* Returns the Message read indicator.
*
* @return bool
*/
public function isRead();

/**
* Sets the read status of a Message.
*
* @param bool $read
* TRUE to set this Message to read.
*
* @return $this
*/
public function setRead($read);

190 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

A continuación, implementamos la clase Message, que extiende a


ContentEntityBase e implementa la interfaz anterior. Recuerda que, aunque el
nombre de sistema o id del tipo de entidad incluye como prefijo el nombre del
módulo, la clase no lo requiere.

La clase tiene una cabecera de comentario con formato annotation, que define las
propiedades de la entidad dentro de la directiva @ContentEntityType.

src/Entity/[Link] (Annotation)

<?php

namespace Drupal\forcontu_entities\Entity;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\user\UserInterface;

/**
* Defines the Message entity.
*
* @ingroup forcontu_entities
*
* @ContentEntityType(
* id = "forcontu_entities_message",
* label = @Translation("Message"),
* bundle_label = @Translation("Message type"),
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\forcontu_entities\MessageListBuilder",
* "views_data" = "Drupal\forcontu_entities\Entity\MessageViewsData",
*
* "form" = {
* "default" = "Drupal\forcontu_entities\Form\MessageForm",
* "add" = "Drupal\forcontu_entities\Form\MessageForm",
* "edit" = "Drupal\forcontu_entities\Form\MessageForm",
* "delete" = "Drupal\forcontu_entities\Form\MessageDeleteForm",
* },
* "access" = "Drupal\forcontu_entities\MessageAccessControlHandler",
* "route_provider" = {
* "html" = "Drupal\forcontu_entities\MessageHtmlRouteProvider",
* },
* },
* base_table = "forcontu_entities_message",
* admin_permission = "administer message entities",
* entity_keys = {
* "id" = "id",
* "bundle" = "type",
* "label" = "subject",
* "uuid" = "uuid",
* "uid" = "user_id",
* "langcode" = "langcode",
* "status" = "status",
* },
* links = {
* "canonical" =
"/admin/structure/forcontu_entities_message/{forcontu_entities_message}",
* "add-page" = "/admin/structure/forcontu_entities_message/add",
* "add-form" =
"/admin/structure/forcontu_entities_message/add/{forcontu_entities_message_type}",
* "edit-form" =
"/admin/structure/forcontu_entities_message/{forcontu_entities_message}/edit",
* "delete-form" =
"/admin/structure/forcontu_entities_message/{forcontu_entities_message}/delete",
* "collection" = "/admin/structure/forcontu_entities_message",
* },
* bundle_entity_type = "forcontu_entities_message_type",
* field_ui_base_route = "entity.forcontu_entities_message_type.edit_form"
* )
*/

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 191
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Las propiedades dentro de @ContentEntityType son similares a las de las entidades


de configuración. Algunas diferencias a destacar son:

- base_table. Es el nombre de la tabla que se creará para almacenar las


entidades de tipo Message.

- bundle_label. Es la etiqueta de los tipos de bundle de esta entidad.

- Dentro de entity_keys:
o 'bundle' = 'type'. El nombre interno del bundle será type, por
lo que crearemos tipos de mensajes. Es similar a los tipos de
contenido, donde el bundle también se identifica como type.
o 'label' = 'subject'. La etiqueta de cada mensaje se
corresponderá con el campo 'subject' (asunto del mensaje). Este
es el único cambio que hemos hecho en este archivo tras
generarlo con Drupal Console.

- bundle_entity_type. Es el nombre del tipo de entidad de configuración


que almacenará la información sobre los bundles.

En el mismo archivo definimos la clase Message, indicando sus propiedades y los


métodos exigidos por la interfaz. Como ya comentamos, las propiedades
adicionales no se añadirán aquí como propiedades.

src/Entity/[Link] (class)

class Message extends ContentEntityBase implements MessageInterface {

use EntityChangedTrait;

/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageInterface
$storage_controller, array &$values) {
parent::preCreate($storage_controller, $values);
$values += array(
'user_id' => \Drupal::currentUser()->id(),
);
}

public function getType() {


return $this->bundle();
}

public function getSubject() {


return $this->get('subject')->value;
}

public function setSubject($subject) {


$this->set('subject', $subject);
return $this;
}

public function getCreatedTime() {


return $this->get('created')->value;
}

public function setCreatedTime($timestamp) {


$this->set('created', $timestamp);
return $this;
}

192 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

public function getOwner() {


return $this->get('user_id')->entity;
}

public function getOwnerId() {


return $this->get('user_id')->target_id;
}

public function setOwnerId($uid) {


$this->set('user_id', $uid);
return $this;
}

public function setOwner(UserInterface $account) {


$this->set('user_id', $account->id());
return $this;
}

public function isPublished() {


return (bool) $this->getEntityKey('status');
}

public function setPublished($published) {


$this->set('status', $published ? TRUE : FALSE);
return $this;
}

public function getUserToId() {


return $this->get('user_to')->target_id;
}

public function setUserToId($uid) {


$this->set('user_to', $uid);
return $this;
}

public function getUserTo() {


return $this->get('user_to')->entity;
}

public function setUserTo(UserInterface $account) {


$this->set('user_to', $account->id());
return $this;
}

public function getContent() {


return $this->get('content')->value;
}

public function setContent($content) {


$this->set('content', $content);
return $this;
}

public function isRead() {


return (bool) $this->getEntityKey('is_read');
}

public function setRead($read) {


$this->set('is_read', $read ? TRUE : FALSE);
return $this;
}

// ...

En el método baseFieldDefinitions(), dentro de la clase de la entidad, definimos


los campos que la entidad tendrá de base. Estos campos, aunque se construyen
como campos de la Field API, no serán modificables por el usuario desde Gestionar
campos. Son campos de base que se almacenan directamente en la tabla base de
la entidad (forcontu_entities_message).

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 193
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

src/Entity/[Link] (campos)

public static function baseFieldDefinitions(EntityTypeInterface


$entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);

$fields['user_id'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Authored by'))
->setDescription(t('The user ID of author of the Message entity.'))
->setRevisionable(TRUE)
->setSetting('target_type', 'user')
->setSetting('handler', 'default')
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'author',
'weight' => 0,
))
->setDisplayOptions('form', array(
'type' => 'entity_reference_autocomplete',
'weight' => 5,
'settings' => array(
'match_operator' => 'CONTAINS',
'size' => '60',
'autocomplete_type' => 'tags',
'placeholder' => '',
),
))
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);

$fields['user_to'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('To'))
->setDescription(t('The user ID of the Message recipient.'))
->setRevisionable(TRUE)
->setSetting('target_type', 'user')
->setSetting('handler', 'default')
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'label' => 'To',
'type' => 'author',
'weight' => 0,
))
->setDisplayOptions('form', array(
'type' => 'entity_reference_autocomplete',
'weight' => 5,
'settings' => array(
'match_operator' => 'CONTAINS',
'size' => '60',
'autocomplete_type' => 'tags',
'placeholder' => '',
),
))
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);

$fields['subject'] = BaseFieldDefinition::create('string')
->setLabel(t('Subject'))
->setDescription(t('The subject of the Message entity.'))
->setSettings(array(
'max_length' => 100,
'text_processing' => 0,
))
->setDefaultValue('')
->setDisplayOptions('view', array(
'label' => 'above',
'type' => 'string',
'weight' => -4,
))
->setDisplayOptions('form', array(
'type' => 'string_textfield',
'weight' => -4,
))

194 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);

$fields['content'] = BaseFieldDefinition::create('text_long')
->setLabel(t('Content'))
->setDescription(t('The content of the Message'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
))
->setDisplayConfigurable('view', TRUE)
->setDisplayOptions('form', array(
'type' => 'text_textfield',
'weight' => 0,
))
->setDisplayConfigurable('form', TRUE);

$fields['is_read'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Read'))
->setDescription(t('A boolean indicating whether the Message is read.'))
->setDefaultValue(FALSE);

$fields['status'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Publishing status'))
->setDescription(t('A boolean indicating whether the Message is
published.'))
->setDefaultValue(TRUE);

$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Created'))
->setDescription(t('The time that the entity was created.'));

$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the entity was last edited.'));

return $fields;
}
}

Los campos que hemos añadido son:

- 'user_to'. Es un campo de tipo 'entity_reference', donde la entidad


('target_type') es 'user'.

- 'subject'. Aquí hemos reaprovechado el campo 'name' creado por


defecto. Es un campo de tipo 'string', que hemos limitado a 100
caracteres.

- 'content'. Es un campo de tipo Texto Largo ('text_long').

- 'is_read'. Es un campo 'boolean' para indicar si el mensaje ha sido leído.

Nota: Hay que tener cuidado con los nombres que asignamos a los campos,
especialmente si estamos trabajando en inglés. Estos nombres serán los nombres
de las columnas en la tabla base, y las consultas generadas sobre la tabla pueden
generar errores si utilizamos nombres reservados de MySQL. Consulta esta tabla
con todas las palabras reservadas:

[Link]

Puedes encontrar más información sobre definición de campos en:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 195
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

[Link]
field-definitions

Definición de rutas y enlaces de menú

Ya hablamos de los dos posibles métodos, rutas estáticas y rutas dinámicas. Aquí
volvemos a utilizar el método generado por Drupal Console.

Los enlaces (links) se definen vía Annotation en la clase Message.

La clase proveedora de rutas se establece en los manejadores de la entidad


(también en el Annotation)

"route_provider" = {
"html" = "Drupal\forcontu_entities\MessageHtmlRouteProvider",

Consulta el archivo src/[Link], generado por Drupal


Console. En principio no tendremos que realizar ninguna modificación en este
archivo. Si queremos cambiar las rutas, lo haremos en la definición Annotation de
la entidad.

En los siguientes archivos se definen los enlaces de menú, enlaces de acción y


pestañas relacionados con la gestión de las entidades:

- forcontu_entities.[Link]
- forcontu_entities.[Link]
- forcontu_entities.[Link]

Clases controladoras de la entidad

Ahora toca definir las páginas que permiten gestionar las entidades de tipo
Message. Volvemos a la sección 'handlers' de la definición en annotation de la
entidad, para ver las clases que debemos crear.

Comenzamos con la página donde se listan, en forma de tabla, las entidades


creadas (src/[Link]). En el método buildHeader() definimos
los encabezados de la tabla, y en buildRow() construimos cada fila, indicando los
valores de cada columna. Los nombres de los elementos de formulario deben
coincidir con los nombres de las propiedades de la entidad.

Como extra, hemos inyectado el servicio '[Link]', para trabajar con


formatos de fecha. Para ello hemos creado las clases __construct() y
createInstance. Repasa la nomenclatura y los parámetros obligatorios, antes de
inyectar los servicios adicionales. Estos parámetros obligatorios vienen
determinados por el constructor de la clase padre.

En la tabla hemos añadido los campos From, To, Subject (reutilizando Name) y
Created. Para recuperar los valores utilizamos los métodos definidos en la clase
Message. El método label() es un método heredado que devuelve la etiqueta de la

196 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

entidad. En nuestro caso, el valor se corresponde con el subject, como definimos


en la entrada annotation.

src/[Link]

<?php

namespace Drupal\forcontu_entities;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Routing\LinkGeneratorTrait;
use Drupal\Core\Url;
use Drupal\Core\Datetime\DateFormatterInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityStorageInterface;

/**
* Defines a class to build a listing of Message entities.
*
* @ingroup forcontu_entities
*/
class MessageListBuilder extends EntityListBuilder {

use LinkGeneratorTrait;

protected $dateFormatter;

public function __construct(EntityTypeInterface $entity_type,


EntityStorageInterface $storage, DateFormatterInterface
$date_formatter) {
parent::__construct($entity_type, $storage);

$this->dateFormatter = $date_formatter;
}

/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container,
EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('entity_type.manager')->getStorage($entity_type-
>id()),
$container->get('[Link]')
);
}

/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['id'] = $this->t('Message ID');
$header['from'] = $this->t('From');
$header['to'] = $this->t('To');
$header['subject'] = $this->t('Subject');
$header['created'] = $this->t('Created');

return $header + parent::buildHeader();


}

/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/* @var $entity \Drupal\forcontu_entities\Entity\Message */

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 197
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

$row['id'] = $entity->id();
$row['from'] = $entity->getOwner()->getAccountName();
$row['to'] = $entity->getUserTo()->getAccountName();

$row['subject'] = $this->l(
$entity->label(),
new Url(
'entity.forcontu_entities_message.edit_form', array(
'forcontu_entities_message' => $entity->id(),
)
)
);
$row['created'] = $this->dateFormatter->format($entity-
>getCreatedTime(), 'short');
return $row + parent::buildRow($entity);
}
}

El formulario MessageForm (src/Form/[Link]) servirá para crear y


editar entidades de tipo Message. En principio no tenemos que realizar ninguna
modificación, aunque podríamos añadir el método validateForm() para incluir
validaciones de campos.

El formulario MessageDeleteForm (src/Form/[Link]) permite


eliminar la entidad. No tenemos que realizar ninguna modificación.

La clase MessageViewsData (src/Entity/MessageViewsData) permite la


integración de la entidad con las vistas. No tenemos que realizar ninguna
modificación.

Bundles

Para poder crear bundles o tipos de mensajes, se define la entidad de configuración


MessageType. Al tratarse de una entidad de configuración, los archivos generados
son los ya estudiados en la unidad anterior. En principio no tenemos que modificar
estos archivos:

- src/Entity/[Link]. Clase principal de la entidad MessageType.


- src/Entity/[Link]. Interfaz de la entidad.
- src/Form/[Link]. Formulario de creación/edición de tipos
de mensajes.
- src/Form/[Link]. Formulario de eliminación de
tipos de mensajes.
- src/[Link]. Proveedor de rutas.
- src/[Link]. Genera la página de administración de
tipos de mensajes.
- config/schema/forcontu_entities_message_type.schema. Esquema de
configuración.

198 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Permisos

Los permisos que se utilizarán para las diferentes operaciones sobre los mensajes,
se declaran en el archivo forcontu_entities.[Link]:

forcontu_entities.[Link]

add message entities:


title: 'Create new Message entities'

administer message entities:


title: 'Administer Message entities'
description: 'Allow to access the administration form to
configure Message entities.'
restrict access: true

delete message entities:


title: 'Delete Message entities'

edit message entities:


title: 'Edit Message entities'

access message overview:


title: 'Access the Message overview page'

view published message entities:


title: 'View published Message entities'

view unpublished message entities:


title: 'View unpublished Message entities'

En el archivo src/[Link] se implementa la lógica


que hace uso de estos permisos. Salvo que queramos realizar algún cambio en esta
lógica de acceso, no tendremos que modificar el archivo.

Theming

En la implementación de la entidad se han añadido las plantillas:

- templates/[Link].
Plantilla para mostrar el listado de mensajes.

- templates/forcontu_entities_message.[Link]. Plantilla utilizada


para mostrar un mensaje. Asociada a esta plantilla, en el archivo
forcontu_entities_message.[Link] se ha implementado una función de
preprocesamiento, que añade como variable la entidad a representar.

Las plantillas se definen en el archivo forcontu_entities.module, a través de la


implementación de hook_theme. También se han añadido sugerencias de plantillas
en función del bundle, el modo de presentación y el id de la entidad.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 199
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

<?php

/**
* Implements hook_theme().
*/
function forcontu_entities_theme() {
$theme = [];
$theme['forcontu_entities_message'] = array(
'render element' => 'elements',
'file' => 'forcontu_entities_message.[Link]',
'template' => 'forcontu_entities_message',
);
$theme['forcontu_entities_message_content_add_list'] = [
'render element' => 'content',
'variables' => ['content' => NULL],
'file' => 'forcontu_entities_message.[Link]',
];
return $theme;
}

/**
* Implements hook_theme_suggestions_HOOK().
*/
function forcontu_entities_theme_suggestions_forcontu_entities_message(array
$variables) {
$suggestions = array();
$entity = $variables['elements']['#forcontu_entities_message'];
$sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.',
'_');

$suggestions[] = 'forcontu_entities_message__' . $sanitized_view_mode;


$suggestions[] = 'forcontu_entities_message__' . $entity->bundle();
$suggestions[] = 'forcontu_entities_message__' . $entity->bundle() . '__'
. $sanitized_view_mode;
$suggestions[] = 'forcontu_entities_message__' . $entity->id();
$suggestions[] = 'forcontu_entities_message__' . $entity->id() . '__' .
$sanitized_view_mode;
return $suggestions;
}

Registrar la nueva entidad

Si, como en nuestro caso, el módulo ya había sido instalado antes de finalizar la
implementación de la nueva entidad, la tabla de la entidad no se habrá creado.

Para registrar la nueva entidad, podemos lanzar el siguiente comando de Drush:

drush updatedb --entity-updates

Si se detectan cambios de configuración de la entidad, el sistema los actualizará:

$ drush updatedb --entity-updates


The following updates are pending:

forcontu_entities_message entity type :


El tipo de entidad Mensaje necesita ser instalado.
forcontu_entities_section entity type :
El tipo de entidad Section necesita ser instalado.
forcontu_entities_message_type entity type :
El tipo de entidad Message type necesita ser instalado.
Do you wish to run all pending updates? (y/n): y
Cache rebuild complete. [ok]
Finished performing updates. [ok]

Podemos usar este comando siempre que realicemos algún cambio de


configuración y no estemos seguros de si afecta a la base de datos.

200 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Crear tipos de mensajes

En nuestra implementación no hemos definido ningún bundle (o tipo de mensaje)


por defecto, así que antes de poder crear un mensaje, tendremos que crear un
tipo. Lo llamaremos Basic, y lo crearemos desde [F31.1a]:

Administración  Estructura  Message Type

F31.1a
Crear tipos de
mensajes
Desde Message Type
podemos crear nuevos
tipos de mensajes o
bundles.

Como se observa en la Figura, cada tipo de mensaje tendrá asociado el conjunto


de opciones típicas de entidades de contenido (Administrar campos, Administrar la
visualización del formulario y Administrar presentación). El funcionamiento es el ya
estudiado a nivel de Site Building.

Crear mensajes

Ahora ya podemos crear nuevos mensajes de tipo Basic, desde [F31.1b]:

Administración  Estructura  Message List [Add Message]

F31.1b
Crear mensajes
Desde Message List
podemos añadir nuevos
mensajes.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 201
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Los mensajes creados se mostrarán en la lista de mensajes, desde donde podemos


realizar las operaciones típicas (ver, editar y eliminar) [F31.1c].

F31.1c
Listado de mensajes
Listado de mensajes y
operaciones.

Tabla base

Los datos se almacenan en la tabla base [F31.1d], ya que todos los campos han
sido definidos como base fields. Los campos adicionales que añadamos a los tipos
de mensajes a través de Administrar campos, o desde programación, se
almacenarán en tablas adicionales.

F31.1d
Tabla base
Tabla donde se almacenan
las entidades creadas.

Integración con vistas

Nuestra implementación tiene total integración con vistas, sin necesidad de añadir
código adicional.

Como ejemplo, hemos implementado la vista Mensajes recibidos, donde se


muestran únicamente los mensajes enviados al usuario actual. Además de los
permisos relacionados con la entidad Mensaje, el usuario también necesita poder
acceder a los perfiles de otros usuarios.

F31.1e
Vistas
Vista Mis Mensajes.

202 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Al crear la vista, podremos añadir el nuevo tipo de entidad Mensaje como


elemento principal de la vista. El resto de la configuración será tal y como hemos
estudiado a nivel de Site Building.

F31.1f
Vistas
Configuración de la vista
Mis mensajes.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 203
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

31.2 Crear un tipo de contenido personalizado

En la implementación de la entidad Message no implementamos ningún bundle o


tipo por defecto. Aunque en este apartado veremos cómo crear un nuevo tipo de
contenido (bundle de la entidad node), es aplicable a cualquier tipo de entidad de
contenido que tenga bundles.
Nuevamente, nos apoyaremos en Drupal Console para generar los archivos (ver
último apartado de esta unidad).

En Drupal 8, la implementación de un nuevo bundle se realiza a través de archivos


de configuración. Durante la instalación del módulo, el sistema se encargará de
generar el nuevo bundle a partir de esa configuración.

En el apartado 19.3, ya vimos que el archivo de esquema de configuración sirve


para describir la estructura de los archivos de configuración de un módulo. Un
módulo puede tener varios archivos de esquema de configuración, aunque lo
habitual es crear un único archivo donde se especifica la estructura de todos los
objetos de configuración. El archivo tendrá el nombre mó[Link], y
estará ubicado en la carpeta del módulo /config/schema.

Veamos los archivos de configuración, y sus correspondientes esquemas, que


podemos utilizar para crear un nuevo tipo de contenido. Estos archivos se ubican
dentro de la carpeta /config/install.

Declaración del tipo de contenido

Archivo: forcontu_entities/config/install/[Link]
Se trata del archivo principal, donde se declara el tipo de contenido y su
configuración por defecto. En el siguiente ejemplo, generado con Drupal Console,
hemos creado el tipo de contenido News (news). Del archivo generado,
recomendamos modificar, al menos, los siguientes parámetros:

- langcode. Si estamos trabajando con el idioma español como base, lo


podemos cambiar a 'es'.
- description. Añade la descripción del tipo de contenido.

langcode: en
status: true
dependencies:
module:
- menu_ui
enforced:
module:
- forcontu_entities
third_party_settings:
menu_ui:
available_menus:
- main
parent: 'main:'
name: News
type: news
description: 'News content type'
help: ''
new_revision: false
preview_mode: 1
display_submitted: true

204 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Los parámetros definidos en este archivo de configuración provienen del archivo


/core/modules/node/config/schema/[Link]:

[Link].*:
type: config_entity
label: 'Content type'
mapping:
name:
type: label
label: 'Name'
type:
type: string
label: 'Machine-readable name'
description:
type: text
label: 'Description'
help:
type: text
label: 'Explanation or submission guidelines'
new_revision:
type: boolean
label: 'Whether a new revision should be created by default'
preview_mode:
type: integer
label: 'Preview before submitting'
display_submitted:
type: boolean
label: 'Display setting for author and date Submitted by post
information'

En el archivo de esquema anterior se definen los parámetros específicos definidos


en el módulo node, para las entidades de contenido. Como, además, se indica que
es de tipo config_entity, tenemos que localizar también los parámetros disponibles
por tratarse de una entidad de configuración. Este esquema lo encontraremos en el
archivo /core/config/schema/[Link]:

config_entity:
type: mapping
mapping:
uuid:
type: string
label: 'UUID'
langcode:
type: string
label: 'Language code'
status:
type: boolean
label: 'Status'
dependencies:
type: config_dependencies
label: 'Dependencies'
third_party_settings:
type: sequence
label: 'Third party settings'
sequence:
type: '[%parent.%parent.%type].third_party.[%key]'
_core:
type: _core_config_info

En la definición del tipo de contenido encontramos una dependencia con el módulo


menu_ui. Bajo third_party_settings se pueden incluir los parámetros de
configuración relacionados con esos módulos declarados como dependencias.

dependencies:
module:
- menu_ui

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 205
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

third_party_settings:
menu_ui:
available_menus:
- main
parent: 'main:'

Y, ¿dónde se definen estos parámetros relacionados con el módulo menu_ui?


Tendremos que consultar los esquemas de configuración del módulo menu_ui. En
el archivo /core/modules/menu_ui/config/schema/menu_ui.[Link],
encontraremos el esquema buscado:

[Link].*.third_party.menu_ui:
type: mapping
label: 'Per-content type menu settings'
mapping:
available_menus:
type: sequence
label: 'Available menus'
sequence:
type: string
label: 'Menu machine name'
parent:
type: string
label: 'Parent'

Desinstalación del tipo de contenido

Para forzar la desinstalación del tipo de contenido al desinstalar el módulo,


podemos añadir una dependencia obligatoria con el módulo donde lo hemos
implementado:

...
dependencies:
module:
- menu_ui
enforced:
module:
- forcontu_entities

Campos adicionales

Archivo: forcontu_entities/config/install/[Link]

Añade un campo Body. Los campos añadidos por esta vía no son campos base, y
se pueden configurar (e incluso eliminar) desde la interfaz. Es por ello que, en los
tipos de contenido, el campo Cuerpo (body) es un campo opcional, mientras que
el campo Título es obligatorio (Título sí es un campo base).

Crearemos un archivo independiente por cada campo adicional que vayamos a


añadir al tipo de contenido.

langcode: en
status: true
dependencies:
config:
- [Link]
- [Link]
module:
- text

206 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

id: [Link]
field_name: body
entity_type: node
bundle: news
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: true
field_type: text_with_summary

El esquema de configuración base del campo lo encontramos en


/core/config/schema/core.data_types.[Link]:

field_config_base:
type: config_entity
mapping:
id:
type: string
label: 'ID'
field_name:
type: string
label: 'Field name'
entity_type:
type: string
label: 'Entity type'
bundle:
type: string
label: 'Bundle'
label:
type: label
label: 'Label'
description:
type: text
label: 'Help text'
required:
type: boolean
label: 'Required field'
translatable:
type: boolean
label: 'Translatable'
default_value:
type: sequence
label: 'Default values'
sequence:
type: [Link].[%parent.%parent.field_type]
label: 'Default value'
default_value_callback:
type: string
label: 'Default value callback'
settings:
type: field.field_settings.[%parent.field_type]
field_type:
type: string
label: 'Field type'

Como se trata de un campo de tipo text_with_summary, tenemos que buscar


su esquema de configuración en el módulo que lo implementa. En este caso se
trata del módulo text del núcleo, y el esquema lo encontraremos en:

/core/modules/text/config/schema/[Link]

El primer esquema que se utiliza es el que aporta las propiedades dentro de


settings. Tenemos que localizar el grupo de configuración especificado:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 207
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

type: field.field_settings.[%parent.field_type]

Por tanto, el esquema correspondiente a text_with_summary será:

field.field_settings.text_with_summary:
type: mapping
label: 'Text (formatted, long, with summary) settings'
mapping:
display_summary:
type: boolean
label: 'Summary input'

Navegando de esta forma por los esquemas de configuración, podemos localizar


todas las propiedades disponibles.

Presentación del tipo de contenido

Archivo:
forcontu_entities/config/install/core.entity_view_display.[Link]

Define las opciones de presentación por defecto (Administrar presentación), para


el modo de presentación Predeterminado (default):

langcode: en
status: true
dependencies:
config:
- [Link]
- [Link]
module:
- text
- user
id: [Link]
targetEntityType: node
bundle: news
mode: default
content:
body:
label: hidden
type: text_default
weight: 101
settings: { }
third_party_settings: { }
links:
weight: 100
hidden: { }

Consulta el esquema de configuración core.entity_view_display.*.*.* en


/core/config/schema/[Link].

Archivo:
forcontu_entities/config/install/core.entity_view_display.[Link]

Define las opciones de presentación por defecto (Administrar presentación), para


el modo de presentación Resumen (teaser):

langcode: en
status: true
dependencies:
config:

208 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

- core.entity_view_mode.[Link]
- [Link]
- [Link]
module:
- text
- user
id: [Link]
targetEntityType: node
bundle: news
mode: teaser
content:
body:
label: hidden
type: text_summary_or_trimmed
weight: 101
settings:
trim_length: 600
third_party_settings: { }
links:
weight: 100
hidden: { }

Presentación del formulario

Archivo:
config/install/core.entity_form_display.[Link]

Define las opciones de presentación del formulario de creación/edición del tipo de


contenido (Administrar la visualización del formulario):

langcode: en
status: true
dependencies:
config:
- [Link]
- [Link]
module:
- path
- text
id: [Link]
targetEntityType: node
bundle: news
mode: default
content:
body:
type: text_textarea_with_summary
weight: 31
settings:
rows: 9
summary_rows: 3
placeholder: ''
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
settings: { }
third_party_settings: { }
path:
type: path
weight: 30
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
settings:
display_label: true
weight: 15
third_party_settings: { }
sticky:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 209
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

type: boolean_checkbox
settings:
display_label: true
weight: 16
third_party_settings: { }
title:
type: string_textfield
weight: -5
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }

Consulta el esquema de configuración core.entity_form_display.*.*.* en


/core/config/schema/[Link].

Registrar el nuevo tipo de contenido o bundle

Para registrar el nuevo tipo de contenido es necesario reinstalar el módulo.


Ten en cuenta que perderás el contenido relacionado que hayas podido crear
previamente.

Referencias en el núcleo

Como ejemplo de creación de nuevos tipos de contenido, se aconseja consultar el


perfil standard del núcleo, donde se definen los tipos de contenido Artículo
(article) y Página básica (page). Puedes localizar estos archivos en
/core/profiles/standard/config/install. Por ejemplo, para el tipo de
contenido 'article', revisa los siguientes archivos:

- [Link]
- [Link].*.yml
- core.entity_form_display.[Link]
- core.entity_view_display.[Link]

Como ejemplo de creación de bundles de otros tipos de entidades de contenido,


se puede consultar, también en el perfil standard, la definición del tipo Basic, dentro
de Bloques de contenido (o bloques personalizados). Consulta estos archivos:

- block_content.[Link]
- [Link].block_content.[Link]
- core.entity_form_display.block_content.[Link]
- core.entity_view_display.block_content.[Link]

Otros enlaces de interés

Programming custom fields into your content type


[Link]
your-content-type

210 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Crear tipos de campos personalizados 31.3

Los tipos de campos se definen a partir de plugins, teniendo en cuenta los


siguientes elementos:

- Definición del tipo de campo, creando un plugin de tipo @FieldType que


extiende a Drupal\Core\Field\FieldItemBase.

- Esquema de configuración del tipo de campo.

- Uno o más controles de formulario (widgets), donde se especifica cómo


es el control de este campo en el formulario de creación/edición de la
entidad. Cada control es un plugin de tipo @FieldWidget.

- Uno o más formateadores (formatters), donde se especifica cómo se


muestra el valor del campo cuando se está presentando la entidad. Cada
formateador es un plugin de tipo @FieldFormatter.

[Link]
fieldformatters

En nuestro módulo Forcontu Entities, vamos a definir el tipo de campo Forcontu


Color (forcontu_entities_color), que almacenará un color en formato RGB
hexadecimal.

@FieldType

El tipo de campo se define con un plugin de tipo @FieldType. Se trata de una clase
que extiende a FieldItemBase, y que se ubica en /src/Plugin/Field/FieldType. Si un
módulo implementa varios tipos de campos, cada clase se ubicará en un archivo
independiente, pero todos dentro de la misma carpeta.

[Link]
ss/FieldItemBase/8

Archivo: /forcontu_entities/src/Plugin/Field/FieldType/[Link]

<?php

namespace Drupal\forcontu_entities\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;

/**
* Plugin implementation of the 'forcontu_entities_color' field type.
*
* @FieldType(
* id = "forcontu_entities_color",
* label = @Translation("Forcontu Color"),
* module = "forcontu_entities",
* description = @Translation("Field to store an RGB color."),
* default_widget = "forcontu_entities_text",

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 211
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

* default_formatter = "forcontu_entities_simple_text"
* )
*/
class ColorItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface
$field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'text',
'size' => 'tiny',
'not null' => FALSE,
),
),
);
}

/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->get('value')->getValue();
return $value === NULL || $value === '';
}

/**
* {@inheritdoc}
*/
public static function
propertyDefinitions(FieldStorageDefinitionInterface $field_definition)
{
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Hex value'));

return $properties;
}
}

En el annotation del plugin (@FieldType) se definen los siguientes parámetros:

- id. Nombre de sistema del tipo de campo. Generalmente añadimos como


prefijo el nombre del módulo.

- label. Etiqueta del campo. Es el nombre del campo que se mostrará desde
administración.

- module. Módulo que implementa el tipo de campo.

- description. Un texto descriptivo, que solo se mostrará en el área de


administración.

- default_widget. El campo debe tener al menos un control. Aquí se


indica el nombre de sistema del plugin que implementa el control por
defecto.

- default_formatter. El campo debe tener al menos un formateador. Aquí


se indica el nombre de sistema del plugin que implementa el formateador
por defecto.

212 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

FieldItemInterface::schema()

Devuelve el esquema del campo, que se utilizará para crear la tabla en la base de
datos. El único valor obligatorio que debemos proporcionar son las columnas que
tendrá la tabla (columns). Opcionalmente podemos añadir claves únicas, índices y
claves ajenas.

[Link]
p/function/FieldItemInterface::schema/8

Nota: los campos computados pueden no requerir la creación de una tabla, ya que
no necesitan almacenar el valor del campo. En este tipo de campos, el valor se
obtiene en tiempo de ejecución, bien calculándolo, o bien obteniéndolo desde otras
fuentes, pero sin almacenarlo. En estos casos, el método schema() debe devolver
un array vacío.

FieldItemInterface::propertyDefinitions()

Este método debe ser implementado obligatoriamente. Devuelve un array con las
propiedades del campo.

[Link]
p/function/FieldItemInterface::propertyDefinitions/8

TypedData::getConstraints ()

El método clásico de validación en Drupal se basa en formularios. Sin embargo, en


Drupal 8, la validación de entidades y campos se ha sustituido por una API de
validación de entidades independiente, basada en constraints o restricciones. De
esta forma, la validación de entidades no depende de que se creen o modifiquen
vía formulario. Veremos más sobre este método de validación en el apartado
31.6.

[Link]
function/TypedData::getConstraints/8

Esquema de configuración

En el esquema de configuración del campo se describen sus propiedades.

Archivo: /forcontu_entities/config/schema/forcontu_entities.[Link]

field.forcontu_entities_color.value:
type: sequence
label: 'Default value'
sequence:
- type: mapping
label: 'Default'
mapping:
value:
type: string
label: 'Value'

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 213
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

@FieldWidget

Cada control de formulario se define como un plugin de tipo @FieldWidget. Se trata


de una clase que extiende a WidgetBase, y que se ubica en
/src/Plugin/Field/FieldWidget. Si un módulo implementa varios controles, cada clase
se ubicará en un archivo independiente, pero todos dentro de la misma carpeta.

[Link]
/WidgetBase/8

Archivo: /forcontu_entities/src/Plugin/Field/FieldWidget/[Link]

<?php

namespace Drupal\forcontu_entities\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Plugin implementation of the 'forcontu_entities_text' widget.
*
* @FieldWidget(
* id = "forcontu_entities_text",
* module = "forcontu_entities",
* label = @Translation("RGB value as #ffffff"),
* field_types = {
* "forcontu_entities_color"
* }
* )
*/
class TextWidget extends WidgetBase {

/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items,
$delta, array $element, array &$form,
FormStateInterface $form_state) {

$value = isset($items[$delta]->value) ? $items[$delta]->value


: '#ffffff';
$element += [
'#type' => 'textfield',
'#default_value' => $value,
'#size' => 7,
'#maxlength' => 7,
];
return ['value' => $element];
}
}

En el annotation del plugin (@FieldWidget) se definen los siguientes parámetros:

- id. Nombre de sistema del control. Generalmente añadimos como prefijo


el nombre del módulo.

- label. Etiqueta del control, tal y como se mostrará en la selección de


control desde administración.

- module. Módulo que implementa el control.

214 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

- field_types. Nombres de sistema (id) de los campos en los que se puede


aplicar este control.

El método formElement() devuelve la estructura del control de formulario para


rellenar el campo. En nuestro ejemplo, el control por defecto es un campo de texto
simple donde se escribirá el valor del color en hexadecimal. La validación del campo
la añadiremos más adelante.

Una vez añadido el campo a una entidad, el control se seleccionará desde


Administrar la visualización del formulario [F31.3a].

F31.3a
Campo personalizado
Selección del control de
formulario.

@FieldFormatter

Cada formateador de campo se define como un plugin de tipo @FieldFormatter.


Se trata de una clase que extiende a FormatterBase, y que se ubica en
/src/Plugin/Field/FieldFormatter. Si un módulo implementa varios formateadores,
cada clase se ubicará en un archivo independiente, pero todos dentro de la misma
carpeta.

[Link]
ass/FormatterBase/8

Archivo:
/forcontu_entities/src/Plugin/Field/FieldFormatter/[Link]

<?php

namespace Drupal\forcontu_entities\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;

/**
* Plugin implementation of the 'forcontu_entities_simple_text' formatter.
*
* @FieldFormatter(

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 215
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

* id = "forcontu_entities_simple_text",
* module = "forcontu_entities",
* label = @Translation("Simple text-based formatter"),
* field_types = {
* "forcontu_entities_color"
* }
* )
*/
class SimpleTextFormatter extends FormatterBase {

/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();

foreach ($items as $delta => $item) {


$elements[$delta] = array(
// We create a render array to produce the desired markup,
// "<p style="color: #hexcolor">The color code ...
// #hexcolor</p>".
// See theme_html_tag().
'#type' => 'html_tag',
'#tag' => 'p',
'#attributes' => array(
'style' => 'color: ' . $item->value,
),
'#value' => $this->t('The color code in this field is
@code', array('@code' => $item->value)),
);
}
return $elements;
}
}

El formateador se seleccionará desde Administrar presentación [F31.3b], para


cada modo de visualización donde se vaya a mostrar el campo.

F31.3b
Campo personalizado
Selección del formateador.

En nuestro ejemplo, el formateador es un párrafo donde se muestra un texto


indicando el código de color. Además, al párrafo se le añade el color como estilo,
de forma que el texto se mostrará en el color seleccionado [F31.3c].

<p style="color: #09d209">The color code in this field is


#09d209</p>

216 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

F31.3c
Campo personalizado
Presentación del campo.

Configuración adicional

Tanto los controles como los formateadores pueden tener configuración


adicional, que se selecciona desde la interfaz. Algunos de los métodos relacionales
con la configuración del plugin son:

- settingsForm(). Formulario para solicitar los valores de configuración.

- defaultSettings(). Devuelve un array asociativo con los valores de


configuración por defecto.

- settingsSummary(). Muestra un resumen con información de los


valores de configuración actuales.

- getSetting(), getSettings(), setSetting(), setSettings(). Métodos


get y set para obtener y almacenar valores de configuración.

Como ejemplo, vamos a añadir configuración al formateador SimpleTextFormatter


implementado. Se podrá seleccionar la etiqueta con la que se envolverá el texto
(<p>, <div> o <span>).

<?php

class SimpleTextFormatter extends FormatterBase {

public function viewElements(FieldItemListInterface $items,


$langcode) {
$elements = [];

foreach ($items as $delta => $item) {


$elements[$delta] = [
'#type' => 'html_tag',
'#tag' => $this->getSetting('formatter_tag'),
'#attributes' => [
'style' => 'color: ' . $item->value,
],
'#value' => $this->t('The color code in this field is
@code', ['@code' => $item->value]),
];
}

return $elements;
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 217
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

public function settingsForm(array $form,


FormStateInterface $form_state) {

$output['formatter_tag'] = [
'#title' => $this->t('HTML tag'),
'#type' => 'select',
'#options' => [
'p' => $this->t('p'),
'div' => $this->t('div'),
'span' => $this->t('span'),
],
'#default_value' => $this->getSetting('formatter_tag'),
];
return $output;
}

public static function defaultSettings() {


return [
'formatter_tag' => 'p',
] + parent::defaultSettings();
}

public function settingsSummary() {


$summary = [];

$formatter_tag = $this->getSetting('formatter_tag');

$summary[] = $this->t('HTML Tag: @tag',


['@tag' => $formatter_tag]);

return $summary;
}
}

No olvides que, además de los métodos relacionados directamente con la


configuración, también tenemos que modificar el método viewElements() para
que utilice el valor de configuración (método getSetting()).

En la Figura [F31.3d] se muestra el resumen de configuración del elemento


(settingsSummary()), junto al botón que da acceso al formulario de
configuración [F31.3e].

F31.3d
Configuración del
plugin
Resumen de la
configuración actual del
plugin.

F31.3e
Configuración del
plugin
Formulario de
configuración del plugin.

Ahora el HTML generado dependerá de la configuración del formateador.

218 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Theming de entidades 31.4

En el apartado 31.1 vimos cómo crear plantillas relacionadas con las entidades.
En este apartado vamos a ver algunos hooks relacionados con la presentación de
entidades.

hook_entity_view()

Permite actuar sobre la presentación de cualquier tipo de entidad, antes de ser


renderizada. El hook recibe $build como parámetro por referencia. Cualquier
modificación sobre este array renderizable, se mostrará directamente sobre la
salida de la entidad.

hook_entity_view(&$build, $entity, $display, $view_mode)

[Link]
n/hook_entity_view/8

Como ejemplo, se puede consultar la implementación de hook_entity_view() en el


módulo comment del núcleo (comment_entity_view). El módulo comment
comprueba que se esté visualizando la entidad en modo RSS ($view_mode ==
'rss'), para añadir un nuevo elemento comments dentro del RSS que es un enlace
a los comentarios de la entidad.

[Link]
on/comment_entity_view/8

hook_ENTITY_TYPE_view()

Funciona exactamente igual que hook_entity_view(), pero actuando sobre un tipo


de entidad específico.

hook_ENTITY_TYPE_view(&$build, $entity, $display, $view_mode)

[Link]
n/hook_ENTITY_TYPE_view/8

hook_entity_view_alter()

Permite actuar sobre la entidad en su conjunto, previo a su renderizado. Este hook


es posterior a hook_entity_view() y la diferencia principal es que se dispone de
un array renderizable más completo.

hook_entity_view_alter(&$build, $entity, $display)

[Link]
n/hook_entity_view_alter/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 219
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

hook_ENTITY_TYPE_view_alter()

Es similar a hook_entity_view_alter(), pero actúa sobre un tipo de entidad


determinado.

hook_ENTITY_TYPE_view_alter(&$build, $entity, $display)

[Link]
n/hook_ENTITY_TYPE_view_alter/8

220 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Programación de taxonomías 31.5

Crear vocabularios

La creación de un vocabulario también se hace, generalmente, desde un archivo


de configuración ubicado en /config/install. Como ejemplo podemos analizar el
archivo [Link] del perfil de instalación standard, que crea
el vocabulario Tags (tags) asociado al tipo de contenido artículo.

Archivo: /config/install/[Link].nombre_vocabulario.yml

langcode: en
status: true
dependencies: { }
name: Tags
vid: tags
description: 'Use tags to group articles on similar topics into
categories.'
hierarchy: 0
weight: 0

El esquema de este archivo de configuración es [Link].*, en:

/core/modules/taxonomy/config/schema/[Link]

Aunque el método anterior es el habitual, también podemos crear vocabularios


desde cualquier otro punto del código, utilizando los métodos create() y save() de
la clase Vocabulary.

[Link]
hp/class/Vocabulary/8

use Drupal\taxonomy\Entity\Vocabulary;

// ...

Vocabulary::create([
'vid' => 'tags',
'name' => 'Tags',
])->save();

Crear términos

Los términos también son una entidad y se pueden crear con los métodos create()
y save() de la clase Term:

[Link]
ss/Term/8

Como ejemplo, analizamos la implementación de hook_install() del módulo Forum


del núcleo, donde se crea el término 'General discussion' asociado al vocabulario
con vid = 'forums':

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 221
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

<?php

/**
* Implements hook_install().
*/
function forum_install() {
// ...
// Create a default forum so forum posts can be created.
$term = Term::create(array(
'name' => t('General discussion'),
'description' => '',
'parent' => array(0),
'vid' => 'forums',
'forum_container' => 0,
));
$term->save();
}

Campos de referencia a taxonomía

Volvemos al perfil de instalación standard para analizar cómo se crea el campo


de Referencia a taxonomía. Se realiza en dos pasos:

1) Se crea el campo para su uso general en entidades de tipo Node:

Archivo: core/profiles/standard/config/install/[Link].field_tags.yml

langcode: en
status: true
dependencies:
module:
- node
- taxonomy
id: node.field_tags
field_name: field_tags
entity_type: node
type: entity_reference
settings:
target_type: taxonomy_term
module: core
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

2) Se instancia el campo anterior, asociándolo al tipo de contenido artículo.

Archivo:
/core/profiles/standard/config/install/[Link].field_tags.yml

langcode: en
status: true
dependencies:
config:
- [Link].field_tags
- [Link]
- [Link]
id: [Link].field_tags

222 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

field_name: field_tags
entity_type: node
bundle: article
label: Tags
description: 'Enter a comma-separated list. For example:
Amsterdam, Mexico City, "Cleveland, Ohio"'
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
tags: tags
sort:
field: _none
auto_create: true
field_type: entity_reference

Acceso a campos de taxonomía

Existen varias formas de acceder a los términos de taxonomía:

1) Cargar una entidad Term conocido su Identificador (tid). Lo haremos


accediendo al almacen del tipo de entidad taxonomy_term.

$term_storage = \Drupal::entityManager->getStorage('taxonomy_term');
$term = $term_storage->load($tid);

2) Si queremos cargar varios términos conocidos sus tids, u obteniendo sus


tids a partir de una consulta con entityQuery:

$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', "tags");
$tids = $query->execute();

$term_storage = \Drupal::entityManager->getStorage('taxonomy_term');
$terms = $term_storage->loadMultiple($tids);

3) Obtener los términos de un campo de Referencia a términos. En el


ejemplo accedemos a los términos del campo field_tags en el nodo
$node.

foreach ($node->field_tags as $item) {


$term = $item->entity;
// $term->id()
// $term->label()
}
}

4) Obtener el árbol de términos de un vocabulario, teniendo en cuenta que


los términos pueden estar jerarquizados:

$term_storage = \Drupal::entityManager()->getStorage('taxonomy_term');
$tree = $term_storage->loadTree('tags');

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 223
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

31.6 Validación de entidades y campos

En Drupal 8 la validación de entidades se ha separado de la validación típica de


formularios. De esta forma, podemos añadir validaciones que se comprobarán
independientemente de cómo se cree o modifique la entidad, ya que los formularios
no son la única vía para ello.

La API de validación de entidades de Drupal utiliza el sistema de validación de


Symfony, basado en constraints (restricciones). Drupal utiliza las restricciones
disponibles en Symfony y añade otras restricciones más específicas. Nosotros
podremos utilizar las constraints existentes o crear nuevas.

Entity Validation API


[Link]

Constraints (restricciones)

Cada constraint se define como un plugin identificado por la etiqueta


@Constraint. La clase validadora recibe la información, comprueba si es válida, y
devuelve el mensaje de error correspondiente.

Las constraints de base que añade el núcleo se encuentran en:

/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint

Las constraints de Symfony se pueden utilizar directamente o extendiendo la clase,


si se requiere realizar alguna modificación. Estas constraints se ubican en:

/vendor/symfony/validator/Constraints/

También puedes encontrar otras constraints en los módulos, tanto del núcleo como
contribuidos (busca @Constraint para localizar todas las disponibles).

LengthConstraint

Como ejemplo, vamos a analizar la constraint Length, gestionada por la clase


LengthConstraint, disponible en:

/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/[Link]

<?php

namespace Drupal\Core\Validation\Plugin\Validation\Constraint;

use Symfony\Component\Validator\Constraints\Length;

/**
* Length constraint.
*
* Overrides the symfony constraint to use Drupal-style replacement patterns.
*

224 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

* @Constraint(
* id = "Length",
* label = @Translation("Length", context = "Validation"),
* type = { "string" }
* )
*/
class LengthConstraint extends Length {

public $maxMessage = 'This value is too long. It should have %limit


character or less.|This value is too long. It should have %limit
characters or less.';
public $minMessage = 'This value is too short. It should have %limit
character or more.|This value is too short. It should have %limit
characters or more.';
public $exactMessage = 'This value should have exactly %limit
character.|This value should have exactly %limit characters.';

/**
* {@inheritdoc}
*/
public function validatedBy() {
return '\Symfony\Component\Validator\Constraints\LengthValidator';
}
}

Se trata de una restricción de longitud sobre una cadena de texto. Puede


indicarse el mínimo (min) o máximo (max) de caracteres que tendrá la cadena (o
ambos valores).

Esta clase sobrescribe a la clase Length de Symfony, que está disponible en:

/vendor/symfony/validator/Constraints/[Link]

Como se indica en el método validatedBy(), la validación se realiza desde la clase


LengthValidator de symfony:

/vendor/symfony/validator/Constraints/[Link]

Las restricciones se pueden aplicar a nivel de entidad, o a nivel de campo.

Validación de entidades

Las validaciones a nivel de entidad se aplican cuando intervienen varios campos o


se tienen que comprobar condiciones complejas más allá del valor de un campo.
Vamos a ver dos casos:

Caso 1: Cuando implementamos un tipo de entidad en nuestro módulo

Por ejemplo, el módulo Comment añade la constraint ContentName:

/core/modules/comment/src/Plugin/Validation/Constraint/
Constraint: [Link]
Validador: [Link]

Esta constraint realiza varias comprobaciones al guardarse un comentario:

- Si el comentario es publicado por un usuario anónimo, se comprueba que


el nombre indicado no coincide con un nombre de usuario registrado.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 225
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

- Si un administrador edita un comentario, comprueba que se especifique


un autor válido.

- El nombre del autor del comentario debe coincidir con el autor del
comentario.

Los campos utilizados en estas comprobaciones son name y uid. En la clase de la


constraint también se definen los mensajes que se devolverán al producirse un
error de validación. Estos mensajes pueden contener parámetros.

<?php

namespace Drupal\comment\Plugin\Validation\Constraint;

use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;

/**
* Supports validating comment author names.
*
* @Constraint(
* id = "CommentName",
* label = @Translation("Comment author name", context = "Validation"),
* type = "entity:comment"
* )
*/
class CommentNameConstraint extends CompositeConstraintBase {

/**
* Message shown when an anonymous user comments using a registered name.
*
* @var string
*/
public $messageNameTaken = 'The name you used (%name) belongs to a
registered user.';
//...
public function coversFields() {
return ['name', 'uid'];
}
}

Veamos un fragmento del método validate():

<?php

class CommentNameConstraintValidator extends ConstraintValidator


implements ContainerInjectionInterface {

protected $context;

public function validate($entity, Constraint $constraint) {


$author_name = $entity->name->value;
$owner_id = (int) $entity->uid->target_id;

// Do not allow unauthenticated comment authors to use a name that is


// taken by a registered user.
if (isset($author_name) && $author_name !== '' && $owner_id === 0) {
$users = $this->userStorage->loadByProperties(array('name' =>
$author_name));
if (!empty($users)) {
$this->context->buildViolation($constraint->messageNameTaken,
array('%name' => $author_name))
->atPath('name')
->addViolation();
}
}
// ...

Cuando no se cumple una condición de la restricción, se genera una violación. El

226 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

contexto ($this->context) recoge todas las violaciones que se generan durante la


validación.

[Link]
[Link]/interface/ExecutionContextInterface/8

Para cada violación (buildViolation), se indica el mensaje que se debe mostrar (en
el ejemplo anterior, $constraint->messageNameTaken). También se pueden añadir
otros parámetros como la propiedad o campo donde se produce la violación
(atPath). Por último, se añade la violación al contexto con addViolation().

[Link]
[Link]/interface/ConstraintViolationBuilderInterface/8

Ahora nos falta relacionar la entidad Comment con la constraint definida. Lo


haremos dentro del Annotation que define la entidad, en el archivo:

/core/modules/comment/src/Entity/[Link]

/**
* Defines the comment entity class.
*
* @ContentEntityType(
* id = "comment",
* ...
* constraints = {
* "CommentName" = {}
* }
* )
*/

Las llaves vacías indican que la constraint no requiere parámetros. Esta definición
será suficiente para que, cuando se guarde una entidad de tipo Comment (creación
o edición), se compruebe primero si se cumple la constraint.

Caso 2: Añadir constraints a entidades de otros módulos

Para añadir restricciones adicionales a entidades de otros módulos recurrimos a


hook_entity_type_alter():

hook_entity_type_alter(array &$entity_types)

[Link]
n/hook_entity_type_alter/8

/**
* Implements hook_entity_type_alter().
*/
function forcontu_entities_entity_type_alter(array &$entity_types) {
$node_definition = $entity_types['node'];
$node_definition->addConstraint('ConstraintName', ['options']);
}

Método validate()

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 227
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Ya hemos comentado que las restricciones se comprueban al guardar una entidad.


También podemos realizar la comprobación directamente sobre la entidad, usando
el método validate(), que devuelve una lista con las violaciones detectadas.

$violations = $entity->validate();

Validación de campos

También podemos añadir constraints a nivel de campo. Cuando se guarda una


entidad, se comprueban las restricciones de cada campo, y se devuelven las
violaciones detectadas.

Caso 1: El tipo de campo se define en nuestro módulo

Cuando definimos un plugin de tipo de campo, podemos incluir las constraints


directamente en el Annotation. Como ejemplo, veamos la definición del tipo de
campo 'file' en el módulo file:

/core/modules/file/src/Plugin/Field/FieldType/[Link]

/**
* Plugin implementation of the 'file' field type.
*
* @FieldType(
* id = "file",
* label = @Translation("File"),
* ...
* constraints = {"ReferenceAccess" = {}, "FileValidation" = {}}
* )
*/

Por ejemplo, podemos consultar la constraint FileValidation en:

/core/modules/file/src/Plugin/Validation/Constraint/

Constraint: [Link]
Validador: [Link]

Las constraints asociadas a un tipo de campo también se pueden añadir dentro del
método propertyDefinitions():

class ListStringItem extends ListItemBase {

public static function propertyDefinitions(FieldStorageDefinitionInterface


$field_definition) {
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Text value'))
->addConstraint('Length', array('max' => 255))
->setRequired(TRUE);

return $properties;
}

TypedData::getConstraints ()

El método getConstraints() devuelve las constraints definidas en un tipo de campo,

228 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

pero también se puede utilizar para definir constraints adicionales.

[Link]
function/TypedData::getConstraints/8

Por ejemplo, dentro del tipo de campo 'telephone', encontramos la siguiente


definición:

/core/modules/telephone/src/Plugin/Field/FieldType/[Link]

public function getConstraints() {


$constraint_manager = \Drupal::typedDataManager()-
>getValidationConstraintManager();
$constraints = parent::getConstraints();

$max_length = 256;
$constraints[] = $constraint_manager->create('ComplexData',
array(
'value' => array(
'Length' => array(
'max' => $max_length,
'maxMessage' => t('%name: the telephone number may not
be longer than @max characters.', array('%name' => $this-
>getFieldDefinition()->getLabel(), '@max' => $max_length)),
)
),
));

return $constraints;
}

Caso 2: El tipo de entidad se define en nuestro módulo

En este caso lo que se define en nuestro módulo no es el tipo de campo, sino un


nuevo tipo de entidad con campos base sobre los que aplicar restricciones.
Añadimos las constraints en el método baseFieldDefinitions(), junto a la declaración
de cada campo:

public static function baseFieldDefinitions(EntityTypeInterface


$entity_type) {

$fields['mail'] = BaseFieldDefinition::create('email')
->setLabel(t('Email'))
->setDescription(t('The email of this user.'))
->setDefaultValue('')
->addConstraint('UserMailUnique')
->addConstraint('UserMailRequired')
->addConstraint('ProtectedUserField');

$fields['age'] = BaseFieldDefinition::create('integer')
->setLabel(t('Age'))
->setDescription(t('The age of this user.'))
->setDefaultValue('')
->addPropertyConstraints('value', [
'Range' => [
'min' => 0,
'max' => 17,
]
]);

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 229
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setPropertyConstraints('value', [
'Length' => [
'max' => 32
]
]);

return $fields;
}

El método addConstraint() se utiliza para añadir constraints que no tienen


parámetros (UserMailUnique, UserMailRequiered, etc.).

El método addPropertyConstraints() se utiliza para añadir constraints con


parámetros, como es el caso de Range o Length.

Caso 3: Añadir una constraint a un campo definido en otro módulo

Por ejemplo, si queremos añadir una restricción al campo title de los nodos,
implementaremos hook_entity_base_field_info_alter():

[Link]
n/hook_entity_base_field_info_alter/8

/**
* Implements hook_entity_base_field_info_alter().
*/
function forcontu_entities_entity_base_field_info_alter(&$fields,
\Drupal\Core\Entity\EntityTypeInterface $entity_type) {

if ($entity_type->id() === 'node') {


$title = $fields['title'];
$title->addPropertyConstraints('value', [
'Length' => [
'min' => 10,
'max' => 50
]
]);
}
}

Método validate()

A nivel de campo también podemos utilizar el método validate(), especificando el


nombre del campo a validar:

$violations = $entity->field_text->validate();

Más información:

Drupal 8 Entity Validation and Typed Data Explained


[Link]

230 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Acceso a rutas y operaciones sobre


31.7
entidades
En el apartado 18.2 (Entidades como parámetros) ya vimos cómo pasar entidades
a través de los parámetros de las rutas. Pasando {user}, por ejemplo, se crea un
objeto de usuario directamente.

Como estas rutas se utilizan, generalmente, para realizar operaciones sobre


entidades, el acceso a las mismas se suele controlar añadiendo la clave
_entity_access.

Por ejemplo, en el formulario de edición de comentarios, comprobamos si el usuario


tiene permisos sobre la operación update ('[Link]'), y en el formulario
de eliminación, sobre la operación delete ('[Link]'), siempre
anteponiendo el nombre de la entidad.

[Link].edit_form:
path: '/comment/{comment}/edit'
defaults:
_title: 'Edit'
_entity_form: '[Link]'
requirements:
_entity_access: '[Link]'
comment: \d+

[Link].delete_form:
path: '/comment/{comment}/delete'
defaults:
_title: 'Delete'
_entity_form: '[Link]'
requirements:
_entity_access: '[Link]'
comment: \d+

En las rutas anteriores también se ha añadido la validación:

comment: \d+

Se trata de una validación adicional basada en expresiones regulares, que


comprueba que el valor del parámetro tenga uno o más dígitos.

Internamente, es el método EntityAccessCheck::access() el que se encarga de


comprobar si el usuario tiene el permiso adecuado.

[Link]
hp/function/EntityAccessCheck::access/8

De la misma forma, podemos utilizar el método access() de la entidad para


comprobar si el usuario actual puede realizar una operación específica:

if ($entity->access('delete')) {
}

if ($entity->access('update')) {
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 231
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

31.8 Hazlo desde la consola

Crear una entidad de contenido (Drupal Console)

drupal generate:entity:content

Genera una nueva entidad de contenido.

drupal generate:entity:content options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:entity:content

Enter the module name [admin_toolbar]:


> forcontu_console

Enter the class of your new content entity [DefaultEntity]:


> BarEntity

Enter the machine name of your new content entity [bar_entity]:


>

Enter the label of your new content entity [Bar entity]:


>

Enter the base-path for the content entity routes


[/admin/structure]:
>

Do you want this (content) entity to have bundles (yes/no) [no]:


> yes

Is your entity translatable (yes/no) [yes]:


> yes

Is your entity revisionable (yes/no) [yes]:


> yes

// generate:entity:config

Enter the base-path for the config entity routes


[/admin/structure]:
>

Generated or updated files

1 - modules/custom/forcontu_console/forcontu_console.[Link]
2 - modules/custom/forcontu_console/forcontu_console.[Link]
3 - modules/custom/forcontu_console/forcontu_console.[Link]
4 - modules/custom/forcontu_console/forcontu_console.[Link]
5 - modules/custom/forcontu_console/src/[Link]
6 - modules/custom/forcontu_console/src/[Link]
7 - modules/custom/forcontu_console/src/Entity/[Link]
8 - modules/custom/forcontu_console/src/Entity/[Link]
9 - modules/custom/forcontu_console/src/[Link]
10 - modules/custom/forcontu_console/src/Entity/[Link]
11 - modules/custom/forcontu_console/src/[Link]
12 - modules/custom/forcontu_console/src/Form/[Link]
13 - modules/custom/forcontu_console/src/Form/[Link]
14 - modules/custom/forcontu_console/src/Form/[Link]
15 - modules/custom/forcontu_console/bar_entity.[Link]
16 - modules/custom/forcontu_console/templates/bar_entity.[Link]

232 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

17 - modules/custom/forcontu_console/src/Form/[Link]
18 - modules/custom/forcontu_console/src/Form/[Link]
19 - modules/custom/forcontu_console/src/Form/[Link]
20 - modules/custom/forcontu_console/src/[Link]
21 - modules/custom/forcontu_console/src/[Link]
22 - modules/custom/forcontu_console/src/Controller/[Link]
23 - modules/custom/forcontu_console/templates/[Link]
24 - modules/custom/forcontu_console/forcontu_console.module
25 - modules/custom/forcontu_console/forcontu_console.module
26 - modules/custom/forcontu_console/config/schema/bar_entity_type.[Link]
27 - modules/custom/forcontu_console/forcontu_console.[Link]
28 - modules/custom/forcontu_console/forcontu_console.[Link]
29 - modules/custom/forcontu_console/src/Entity/[Link]
30 - modules/custom/forcontu_console/src/Entity/[Link]
31 - modules/custom/forcontu_console/src/[Link]
32 - modules/custom/forcontu_console/src/Form/[Link]
33 - modules/custom/forcontu_console/src/Form/[Link]
34 - modules/custom/forcontu_console/src/[Link]

[Link]
console/content/es/commands/[Link]

Crear un nuevo tipo de contenido (Drupal Console)

drupal generate:entity:bundle

Genera un nuevo tipo de contenido (bundle de node).

drupal generate:entity:bundle options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:entity:bundle

// Welcome to the Drupal Content Type generator


Enter the module name [admin_toolbar]:
> forcontu_console

Enter the machine name of your new content type [default]:


> news

Enter the human-readable name of your new content type [default]:


> News

Do you confirm generation? (yes/no) [yes]:


> yes

Generated or updated files

1 - modules/custom/forcontu_console/config/install/core.entity_form_display.[Link]
2 - modules/custom/forcontu_console/config/install/core.entity_view_display.[Link]
3 - modules/custom/forcontu_console/config/install/core.entity_view_display.[Link]
4 - modules/custom/forcontu_console/config/install/[Link]
5 - modules/custom/forcontu_console/config/install/[Link]

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 233
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

Crear un tipo de campo completo (Drupal Console)

drupal generate:plugin:field

Genera un nuevo tipo de campo completo, incluyendo los plugins de tipo de campo,
control (widget) y formateador (formatter).

drupal generate:plugin:field options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:plugin:field

// Welcome to the Drupal Field Plugin generator


Enter the module name [admin_toolbar]:
> forcontu_console
Field type plugin class name [ExampleFieldType]:
> Baz
Enter the field type plugin label [Baz]:
>
Enter the field type plugin id [baz]:
>
Enter the field type plugin description [My Field Type]:
> Baz field type
Enter the field widget plugin class name [ExampleWidgetType]:
> BazWidget
Enter the field widget plugin label [Baz widget]:
>
Enter the field widget plugin id [baz_widget]:
>
Enter the field formatter plugin class name [ExampleFormatterType]:
> BazFormatter
Enter the field formatter plugin label [Baz formatter]:
>
Enter the field formatter plugin id [baz_formatter]:
>
Enter the field type the formatter and widget plugin can be used with
[baz]:
> string
Enter the default field widget of the field type plugin [baz_widget]:
>
Enter the default field formatter of field type plugin [baz_formatter]:
>
Do you confirm generation? (yes/no) [yes]:
> yes

// generate:plugin:fieldtype
// generate:plugin:fieldwidget
// generate:plugin:fieldformatter
// cache:rebuild

Rebuilding cache(s), wait a moment please.


[OK] Done clearing cache(s).

Generated or updated files

1 - modules/custom/forcontu_console/src/Plugin/Field/FieldType/[Link]
2 - modules/custom/forcontu_console/src/Plugin/Field/FieldWidget/[Link]
3 - modules/custom/forcontu_console/src/Plugin/Field/FieldFormatter/[Link]

[Link]
console/content/es/commands/[Link]

234 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 31. Entidades II: Entidades de contenido

drupal generate:plugin:fieldtype

Genera únicamente un plugin de tipo de campo.

[Link]
console/content/es/commands/[Link]

drupal generate:plugin:fieldwidget

Genera únicamente un plugin de control (widget) de campo.

[Link]
console/content/es/commands/[Link]

drupal generate:plugin:fieldformatter

Genera únicamente un plugin formateador (formatter) de campo.

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 235
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

32 Formatos de texto y filtros


En Drupal, un filtro es una regla que se utiliza para transformar el
Comparativa D8/D7
texto introducido por el usuario. Por ejemplo, es posible añadir un
filtro para eliminar cadenas de texto que encajen con un patrón En Drupal 8, los filtros se implementan
previamente definido. También es posible realizar una sustitución de como plugins, así que la programación es
cadenas o añadir texto adicional. totalmente diferente a la de Drupal 7.

Los filtros se aplican haciendo uso de los formatos de texto. Al indicar


que un texto tendrá un formato de texto determinado, estamos
estableciendo un conjunto de reglas de transformación que se
aplicarán al texto antes de mostrarlo en el sitio web.

Los formatos de texto también están relacionados con los Editores


de texto Wysiwyg. Drupal 8 incluye en el núcleo el editor CKEditor,
facilitándose así la edición de contenidos HTML.

En esta unidad veremos cómo crear filtros y formatos de textos


personalizados.

32
Contenidos de la Unidad
32.1 Administración de formatos de texto y filtros
32.2 Crear un filtro personalizado
32.3 Crear un formato de texto

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 239
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

Administración de formatos de texto y


32.1
filtros
En la Unidad 13 del Experto en Drupal 8 Site Building estudiamos cómo
trabajar con formatos de texto y filtros desde la interfaz.

Un filtro es una regla que se utiliza para transformar o manipular el texto


introducido por el usuario. Cada filtro tiene un objetivo diferente. Por ejemplo, es
posible añadir un filtro para eliminar cadenas de texto que encajen con un patrón
previamente definido. También es posible realizar una sustitución de cadenas o
añadir texto adicional.

En Drupal los filtros se aplican haciendo uso de los formatos de texto. Al indicar
que un texto tendrá un formato de texto determinado, estamos estableciendo un
conjunto de reglas de transformación que se aplicarán al texto antes de mostrarlo
en el sitio web. Es importante tener en cuenta que los filtros se aplican en el
momento de generar la presentación del sitio (cargar la página en el navegador),
pero internamente la base de datos almacena en todo momento el texto original
introducido por el usuario, sin aplicarle ningún tipo de transformación.

Los formatos de texto son, por tanto, un conjunto de filtros que se aplicarán al
texto de forma ordenada. El correcto orden de los filtros será fundamental para
obtener el texto resultante deseado.

Algunos de los filtros disponibles por defecto son:

- Mostrar cualquier HTML como texto sin formato. Muestra el texto


como texto plano, eliminando cualquier tipo de formato.

- Limitar las etiquetas HTML permitidas. Se encarga de eliminar


etiquetas HTML no permitidas. Se configura indicándole la lista de
etiquetas HTML permitidas, de forma que el filtro eliminará el resto de
etiquetas no incluidas en la lista.

- Convertir saltos de línea en HTML. Convierte los saltos de línea en


etiquetas <br> o <p> de HTML, dependiendo de si encuentra un único
salto de línea o un salto de línea doble, respectivamente.

- Convertir las URL en enlaces. Convierte las direcciones web y de


correo electrónico en enlaces HTML (<a href="...">...</a>).

- Corregir HTML defectuoso o incompleto. Corrige el HTML defectuoso


o incompleto. Por ejemplo, si el usuario olvida cerrar una etiqueta <p>.

- Alinear imágenes. Utiliza el atributo data-align en las etiquetas <img>


para alinear las imágenes.

Inicialmente Drupal integra cuatro formatos de texto: Basic HTML (HTML básico),
Restricted HTML (HTML restringido), Full HTML (HTML completo) y Plain text (texto
plano).

240 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

Para ver y configurar los formatos de texto disponibles en el sitio debemos acceder
a [F32.1a]:
URL Formatos de texto
Administración  Configuración  Autoría del contenido /admin/config/content/
 Formato y editores de texto formats

F32.1a
Formatos de texto
Por defecto Drupal cuenta
con los formatos de texto
Filtered HTML, Full
HTML y Plain text.

Desde el listado de formatos de texto podemos acceder a la configuración de cada


uno de ellos. Destacamos las siguientes opciones de configuración [F32.1b]:

- Editor de texto, botones y extensiones. Permite seleccionar el Editor


Wysiwyg que se utilizará con este formato de texto. En el núcleo de Drupal
8 sólo está disponible CKEditor, pero más adelante se podrán incorporar
otros editores a través de módulos contribuidos. Una vez seleccionado el
editor podremos indicar los botones que se mostrarán en la barra de
herramientas y configurar otras opciones del editor.

- Filtros activos. Indica qué filtros se aplicarán al utilizar el formato de


texto.

- Oden de procesamiento de los filtros. El resultado final mostrado


puede diferir si utilizamos los mismos filtros en diferente orden. Será
importante, por tanto, aplicar los filtros en el orden correcto. Para ordenar
los filtros, utiliza el selector de peso o arrastra el filtro a la posición
deseada.

- Opciones de filtro. Algunos de los filtros activos pueden requerir una


configuración adicional. En opciones de filtro se agruparán las opciones
de configuración en pestañas, para cada uno de los filtros activos que
sean configurables.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 241
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

F32.1b
Configuración de
formatos de texto
Para cada formato de texto
podemos especificar los
filtros que se aplicarán y
los roles que podrán hacer
uso de él.

También sabemos que algunos filtros pueden tener opciones adicionales de


configuración [F32.1c]:

F32.1c
Formatos de texto.
Configurar filtros
Algunos filtros tienen
opciones de configuración
adicionales.

242 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

Crear un filtro personalizado 32.2

En Drupal 8, los filtros son un tipo de plugin (@Filter) que extienden a la clase
FilterBase (que implementa la interfaz FilterInterface).

[Link]
s/FilterBase/8

La clase FilterBase implementa la interfaz FilterInterface, que es donde


encontraremos una explicación más detallada de los métodos y propiedades
disponibles, así como de las propiedades que podemos incluir en el Annotations del
plugin.

[Link]
/interface/FilterInterface/8

En esta unidad crearemos el módulo Forcontu Filters (forcontu_filters), y


definiremos el filtro FilterBlackList (filter_black_list). Inicialmente, el filtro
sustituirá todas las palabras de una lista negra, por '*****'. Una vez tengamos esta
funcionalidad básica, la ampliaremos añadiendo un formulario de configuración al
filtro.

Nuestro filtro se ubicará en: /forcontu_filters/src/Plugin/Filter/[Link]

<?php

namespace Drupal\forcontu_filters\Plugin\Filter;

use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;

/**
* Provides a filter to replace words from a black list.
*
* @Filter(
* id = "filter_black_list",
* title = @Translation("Black list filter"),
* description = @Translation("Replaces all words from a black list"),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE
* )
*/
class FilterBlackList extends FilterBase {

/**
* {@inheritdoc}
*/
public function process($text, $langcode) {

$black_list = ['foo', 'bar'];

$filtered_text = str_replace($black_list, '*****', $text);

return new FilterProcessResult($filtered_text);


}
}

Dentro del Annotations, hemos definido el id del plugin, el título y la descripción.


También hemos tenido que indicar el tipo (type) de filtro, que puede ser:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 243
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

- FilterInterface::TYPE_HTML_RESTRICTOR: Filtros que restringen el


uso de etiquetas HTML y atributos. Por ejemplo, el filtro 'filter_html', que
permite indicar qué etiquetas HTML serán eliminadas.

- FilterInterface::TYPE_MARKUP_LANGUAGE: Filtros que generan


código HTML a partir de código no HTML. Por ejemplo, el filtro 'filter_url',
que convierte URLs en enlaces HTML.

- FilterInterface::TYPE_TRANSFORM_REVERSIBLE: Filtros que


realizan transformaciones reversibles. Por ejemplo, el filtro 'filter_align',
que alinea algunos elementos del texto. Se considera reversible cuando
el editor WYSIWYG tiene un plugin para revertir el resultado.

- FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE: Filtros que


realizan transformaciones irreversibles. Por ejemplo, el filtro
'filter_htmlcorrector', que corrige HTML defectuoso. Se considera
irreversible cuando el editor WYSIWYG no dispone de un plugin para
revertir el resultado.

Nuestro filtro será de tipo FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE.

Otros parámetros que podemos especificar dentro del Annotations son:

- status. Estado por defecto cuando se crean nuevas instancias del filtro.
Por ejemplo, cuando se crea un nuevo formato de texto. El valor por
defecto es FALSE, de forma que estará desactivado inicialmente.
- weight. Peso con respecto a otros filtros. El valor por defecto es 0.
- settings. Valores de configuración del filtro por defecto.

En la clase que implementa nuestro filtro, el único método que es necesario


implementar es process():

FilterInterface::process($text, $langcode)

[Link]
/function/FilterInterface::process/8

El método process() recibe como parámetros el texto ($text) a filtrar, y el idioma


en que está ese texto, por si necesitamos hacer alguna distinción. Como resultado
debe devolver el texto filtrado, en forma de objeto de tipo FilterProcessResult.

Para probar el resultado, instalaremos el módulo y activaremos el filtro en el


formato de texto HTML Completo [F32.2a].

244 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

F32.2a
Activar el filtro
Activaremos el filtro en el
formato de texto HTML
Completo.

Ahora crearemos un nodo con texto de prueba, asegurándonos de que el cuerpo


está en formato HTML Completo y que incluye varias veces las palabras de la lista
negra (foo y bar) [F32.2b].

F32.2b
Texto filtrado
Ejemplo de texto filtrado.

Formulario de configuración del filtro

Vamos a añadir un formulario de configuración al filtro, añadiendo un campo que


permitirá que el usuario edite la lista negra de palabras a sustituir.

El formulario se añade a través del método settingsForm(). Estos son algunos de


los cambios que hemos realizado:

- Se requiere una cláusula use que incluya FormStateInterface.

- Las variables de configuración tendrán el mismo nombre que el campo


del formulario. Se accede a ellas a través de la propiedad settings
($this->settings['black_list']).

- En el Annotations hemos añadido los valores por defecto en settings.

- Hemos modificado el método process() para que lea la lista negra desde
la variable de configuración.

<?php

namespace Drupal\forcontu_filters\Plugin\Filter;

use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\Core\Form\FormStateInterface;

/**
* Provides a filter to replace words from a black list.
*

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 245
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

* @Filter(
* id = "filter_black_list",
* title = @Translation("Black list filter"),
* description = @Translation("Replaces all words from a black list"),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
* settings = {
* "black_list" = "foo bar",
* }
* )
*/
class FilterBlackList extends FilterBase {

/**
* {@inheritdoc}
*/
public function process($text, $langcode) {

$black_list = explode(' ', $this->settings['black_list']);

$filtered_text = str_replace($black_list, '*****', $text);

return new FilterProcessResult($filtered_text);


}

public function settingsForm(array $form,


FormStateInterface $form_state) {

$form['black_list'] = [
'#type' => 'textfield',
'#title' => $this->t('Black list'),
'#default_value' => $this->settings['black_list'],
'#description' => $this->t('A list of words to be banned
(separated by space)'),
];

return $form;
}
}

El resultado se muestra ahora en la configuración del formato de texto donde


hemos activado el filtro [F32.2c]. Esta configuración del filtro será específica para
este formato de texto. Comprueba el funcionamiento añadiendo nuevas palabras
a la lista negra, y utilizándolas en el texto de prueba.

F32.2c
Configuración del filtro
Formulario de
configuración adicional del
filtro.

Enlaces de interés:

Filter API overview


[Link]

Creating a custom filter in Drupal 8


[Link]

246 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

Crear un formato de texto 32.3

Para ver cómo se crea un nuevo formato de texto desde un módulo, analizaremos
el código de perfil de instalación Standard, donde se crean los formatos de texto
por defecto (basic_html, full_html y restricted_html).

Para crear un nuevo formato de texto, añadimos el archivo:

/config/install/[Link].nombre_formato.yml

siguiendo el esquema [Link].*, especificado por el módulo filter en:

/core/modules/filter/config/schema/[Link]

El perfil standard implementa estos archivos para cada uno de los formatos de
texto:

/core/profiles/standard/config/install/[Link].basic_html.yml
/core/profiles/standard/config/install/[Link].full_html.yml
/core/profiles/standard/config/install/[Link].restricted_html.yml

Como ejemplo, mostramos la definición para el formato Full HTML:

langcode: en
status: true
dependencies:
module:
- editor
name: 'Full HTML'
format: full_html
weight: 1
roles:
- administrator
filters:
filter_align:
id: filter_align
provider: filter
status: true
weight: 8
settings: { }
filter_caption:
id: filter_caption
provider: filter
status: true
weight: 9
settings: { }
filter_htmlcorrector:
id: filter_htmlcorrector
provider: filter
status: true
weight: 10
settings: { }
editor_file_reference:
id: editor_file_reference
provider: editor
status: true
weight: 11
settings: { }

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 247
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

Dentro de "filters" se añaden los filtros que estarán activos, con status: true. Si un
filtro tiene configuración específica, podemos añadir también los valores por
defecto.

Asignar el editor WYSIWYG

Los formatos de texto que utilizan el editor WYSIWYG requieren una configuración
adicional, en el archivo:

/config/install/[Link].nombre_formato.yml

Siguiendo el esquema:

/core/modules/ckeditor/config/schema/[Link]

El perfil standard implementa estos archivos para los formatos Full HTML y Basic
HTML:

/core/profiles/standard/config/install/[Link].full_html.yml
/core/profiles/standard/config/install/[Link].basic_html.yml

Como ejemplo, vemos la configuración para el editor en formato Full HTML. Entre
otras opciones, se configuran los iconos que se activarán en el editor.

langcode: en
status: true
dependencies:
config:
- [Link].full_html
module:
- ckeditor
format: full_html
editor: ckeditor
settings:
toolbar:
rows:
-
-
name: Formatting
items:
- Bold
- Italic
- Strike
- Superscript
- Subscript
- '-'
- RemoveFormat
-
name: Linking
items:
- DrupalLink
- DrupalUnlink
-
name: Lists
items:
- BulletedList
- NumberedList
-
name: Media
items:
- Blockquote

248 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 32. Formatos de texto y filtros

- DrupalImage
- Table
- HorizontalRule
-
name: 'Block Formatting'
items:
- Format
-
name: Tools
items:
- ShowBlocks
- Source
plugins:
stylescombo:
styles: ''
image_upload:
status: true
scheme: public
directory: inline-images
max_size: ''
max_dimensions:
width: 0
height: 0

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 249
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

33 Archivos e imágenes
En esta unidad analizamos las funciones, clases y servicios que nos
Comparativa D8/D7
permiten manipular archivos e imágenes.
Existen muchas similutes entre Drupal 8 y
Drupal 7, pero la nueva API orientada a
Veremos qué son los archivos gestionados y no gestionados y cómo
objetos hace que sea necesario el estudio
trabajar con ellos desde formularios.
de esta unidad en profundidad.

También veremos cómo controlar el acceso a archivos privados. ´

Por último, veremos cómo presentar imágenes usando estilos de


imagen específicos, y cómo crear efectos de imagen personalizados.

Contenidos de la Unidad
33.1 El sistema de archivos de Drupal

33
33.2 Funciones de archivos
33.3 La entidad File
33.4 Hooks relacionados con archivos
33.5 Formularios con archivos
33.6 Control de permisos sobre archivos
33.7 Presentación de imágenes con estilos
33.8 Efecto de imagen personalizado
33.9 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 253
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.1 El sistema de archivos de Drupal

Archivos públicos y archivos privados

En el curso de Experto en Drupal 8 Site Building (Unidad 15), vimos que Drupal
permite almacenar los archivos como públicos o como privados.

Un archivo público tiene una ruta que equivale a la carpeta real donde está
almacenado:

[Link]

Por ello, los archivos públicos son accesibles desde Internet y pueden ser
visualizados o descargados por cualquier usuario si conoce la URL o si están
enlazados desde nuestra web.

Por el contrario, un archivo privado se aloja en una carpeta "secreta" que no es


accesible directamente desde la web. La ruta para descargar el archivo se genera
dinámicamente y es virtual (no existe físicamente), lo que permite al sistema
controlar el acceso antes de entregar el archivo. La ruta de un archivo privado es
del tipo:

[Link]

URIs

Cuando trabajamos con archivos, las rutas se expresan en forma de URI con la
estructura scheme://target, donde schema representa el esquema o ubicación
(public, private, temporary, etc.) y target la ruta relativa al archivo.

- public://[Link]
- public://foo/bar/[Link]
- private://[Link]

Por ejemplo, si el esquema public se corresponde con /sites/default/files, la


URI public://foo/bar/[Link] el archivo se encuentra realmente en:

/sites/default/files/foo/bar/[Link]

Configuración del sistema de archivos

Desde la configuración del sistema de archivos podemos comprobar qué rutas se


han definido para almacenar los archivos subidos o generados en el sitio:

Administración  Configuración  Multimedia 


Sistema de archivos

La ruta por defecto del sistema público de archivos es sites/default/files. Esta

254 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

carpeta por defecto puede cambiarse en el archivo de configuración [Link],


como veremos a continuación.

El método predeterminado de descarga, y el único disponible inicialmente, es el


método público (Archivos locales públicos servidos por el servidor web). Esto quiere
decir que todos los archivos estarán disponibles usando HTTP, no pudiéndose
controlar el acceso a ellos, que estará abierto para todos los usuarios que conozcan
la URL al archivo.

Si queremos tener un control de acceso sobre los archivos del sitio, podemos
indicar una ruta para el sistema privado de archivos, generalmente fuera del
acceso directo desde la web. En Drupal 7, esta ruta se podía configurar desde la
interfaz, pero en Drupal 8 es necesario indicar la ruta en el archivo de configuración
[Link].

La ruta indicada debe ser absoluta y estar fuera del directorio donde has
instalado Drupal (y fuera del acceso directo desde la web). Por ejemplo, si tu
instalación de Drupal está en:

/home/username/public_html

La carpeta de archivos privados podría ir ubicada en /home/username/private,


nunca dentro de public_html.

Recuerda que, para poder modificar el archivo [Link], debes asignarle


permisos de escritura.

/**
* Private file path:
*
* A local file system path where private files will be stored. This directory
* must be absolute, outside of the the Drupal installation directory and not
* accessible over the web.
*
* Note: Caches need to be cleared when this value is changed to make the
* private:// stream wrapper available to the system.
*
* See [Link] for more information about
* securing private files.
*/

$settings['file_private_path'] = '/home/username/private';

Tras modificar el archivo [Link], vacía la caché del sitio. De esta forma, se
creará la carpeta y el archivo .htaccess que la protege.

Una vez vaciada la caché, vuelve a la configuración del Sistema de archivos.


Ahora se mostrará una nueva opción para el Método predeterminado de
descarga [F33.1a]:

- Archivos locales públicos servidos por el servidor web.


- Archivos privados locales servidos por Drupal.

El uso de archivos privados proporciona control de acceso a los archivos del sitio,
aunque es menos eficiente que el método público.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 255
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

F33.1a
Método predeterminado
de descarga
Selección del método
predeterminado de
descarga.

Modificar la ubicación del sistema público de archivos

Siguiendo los mismos pasos también podemos modificar la ubicación original del
sistema público de archivos. Sólo tenemos que activar y modificar la variable
correspondiente en el archivo [Link]:

/**
* Public file path:
*
* A local file system path where public files will be stored. This directory
* must exist and be writable by Drupal. This directory must be relative to
* the Drupal installation directory and be accessible over the web.
*/
$settings['file_public_path'] = 'sites/default/files';

En este caso tendremos en cuenta:

- Si se va cambiar el sistema público de archivos se recomienda hacerlo al


principio del proyecto, nada más instalar Drupal.

- El sistema público de archivos sí tiene que ser accesible desde la web.

- La ruta se indica de forma relativa a la instalación de Drupal (sin barra /


delante).

- Necesitaremos mover a la nueva carpeta todos los archivos que ya se


hayan subido o generado en la carpeta original sites/default/files.

- Recuerda vaciar la caché una vez realizados los cambios.

256 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Archivos gestionados y no gestionados

En Drupal distinguimos entre archivos gestionados (managed) y archivos no


gestionados (unmanaged).

Los archivos gestionados son aquellos que se manejan como entidades, a


través de Entity API. De ellos se guarda un registro completo en la base de datos,
con un correspondiente ID. Cuando se realizan operaciones sobre archivos
gestionados, se ejecutan hooks relacionados.

Los archivos no gestionados son archivos que no se almacenan como entidades.


Las operaciones y funciones que se aplican sobre ellos son las típicas de gestión
de archivos desde PHP. Cuando se realizan operaciones sobre estos archivos, no
se realizan cambios en la base de datos ni se ejecutan hooks relacionados.

Clase FileSystem

El servicio 'file_system', gestionado por la clase \Drupal\Core\File\FileSystem,


facilita algunos métodos para trabajar con el sistema de archivos:

[Link]
leSystem/8

Algunos de los métodos disponibles son:

- FileSystem::basename($uri). Devuelve el nombre del archivo dada una


ruta en forma de URI.

[Link]
p/function/FileSystem::basename/8

- FileSystem::uriScheme($uri). Devuelve el esquema a partir de una URI.

[Link]
p/function/FileSystem::uriScheme/8

- FileSystem::validScheme($scheme). Comprueba que el esquema de


una URI sea válido.

[Link]
p/function/FileSystem::validScheme/8

- FileSystem::dirname($uri). Obtiene el nombre del directorio dada una


ruta.

[Link]
p/function/FileSystem::dirname/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 257
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

<?php

$file_system = \Drupal::service('file_system');

$uri = 'private://foo/bar/[Link]';

//devuelve: 'private://foo/bar'
$directory = $file_system->dirname($uri);

//devuelve: 'private'
$scheme = $file_system->uriScheme($uri);

//devuelve: '[Link]
$filename = $file_system->basename($uri);

- FileSystem::mkdir($uri, $mode = NULL, $recursive = FALSE). Crea un


directorio.

Con $recursive a TRUE podemos crear la estructura de directorios


completa especificada en la ruta, siempre que no existan.

En $mode se indica el valor entero con los permisos. Si lo dejamos a NULL,


se utilizarán los permisos por defecto (los mismos asignados durante la
instalación de Drupal al resto de directorios).

[Link]
p/function/FileSystem::mkdir/8

- FileSystem::rmdir($uri, …). Elimina un directorio.

[Link]
p/function/FileSystem::rmdir/8

- FileSystem::chmod($uri, $mode). Establece los permisos de un archivo


o directorio. En $mode se indica el valor entero con los permisos. Consulta
el comando chmod de Linux explicado en el apartado 3.6.

[Link]
p/function/FileSystem::chmod/8

- FileSystem::unlink($uri, …). Elimina un archivo.

[Link]
p/function/FileSystem::unlink/8

<?php

$file_system = \Drupal::service('file_system');

// crea el directorio foo


$file_system->mkdir('public://foo');

// crea los directorios /bar/baz con permisos 755


$file_system->mkdir('public://bar/baz', 0755, TRUE);

// elimina el directorio foo


$file_system->rmdir('public://foo');

258 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Funciones de archivos 33.2

Las funciones disponibles para trabajar con archivos, generalmente no


gestionados, se encuentran en el archivo /core/includes/[Link]. Ten en cuenta
que algunas de estas funciones han quedado obsoletas y, en su lugar, se debe usar
el servicio correspondiente.

[Link]

- file_default_scheme(). Devuelve el esquema por defecto ('public', por


defecto).

[Link]
ult_scheme/8

- file_prepare_directory(&$directory, $options). Comprueba si un


directorio existe y se puede escribir. En $options podemos indicar los
valores:
o FILE_CREATE_DIRECTORY, para crear el directorio si no existe.
o FILE_MODIFY_PERMISSIONS, para modificar los permisos y
permitir la escritura.

[Link]
are_directory/8

- file_scan_directory($dir, $mask, $options = array(), $depth = 0).


Encuentra todos los archivos (visibles) en el directorio $dir, que cumplen
con la máscara o expresión regular definida en $mask.

[Link]
_directory/8

//localiza todas las plantillas en el módulo forum


$directory = \Drupal::root() . "/core/modules/forum";
$files = file_scan_directory($directory, '/\.twig$/');

- file_save_upload($form_field_name, $validators = array(), $destination


= FALSE, $delta = NULL, $replace = FILE_EXISTS_RENAME). Guarda un
archivo subido a otra localización. El archivo se sube inicialmente como
temporal, y solo cuando se guarda definitivamente o se registra su uso,
pasa a permanente. Esta función se utiliza para archivos gestionados.

[Link]
ile_save_upload/8

- file_unmanaged_save_data($data, $destination = NULL, $replace =


FILE_EXISTS_RENAME). Guarda un archivo subido a otra localización. Se
utiliza para archivos no gestionados, de forma que no se almacena
información en la base de datos ni se ejecutan los hooks relacionados con
archivos.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 259
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

[Link]
anaged_save_data/8

- file_delete($fid) y file_delete_multiple(array $fids). Permiten


eliminar archivos gestionados. Se elimina tanto el archivo como la
información almacenada en la base de datos. En lugar de estas funciones,
se recomienda eliminar las entidades desde el almacén correspondiente,
como veremos en el próximo apartado.

[Link]
[Link]
e_multiple/8

- file_unmanaged_delete($path). Elimina un archivo no gestionado. Se


elimina el archivo directamente, sin realizar cambios en la base de datos
o invocar a hooks relacionados.

Nota: Esta función no debe utilizarse con un archivo gestionado, ya que


se perdería el archivo físico, pero estaríamos dejando el registro en la
base de datos.

[Link]
anaged_delete/8

- file_unmanaged_delete_recursive($path, …). Elimina todos los


archivos y directorios en la ruta especificada. Solo elimina archivos visibles
y con permisos de escritura.

[Link]
anaged_delete_recursive/8

- file_unmanaged_copy($source, $destination = NULL, $replace =


FILE_EXISTS_RENAME). Copia un archivo no gestionado a otra
localización. Cuando el archivo de destino existe, podemos establecer uno
de estos dos métodos ($replace):
o FILE_EXISTS_ERROR. Devuelve un error indicando que el
archivo ya existe. No se realiza la copia.
o FILE_EXISTS_REPLACE. Reemplaza el archivo existente.
o FILE_EXISTS_RENAME. Renombra el archivo añadiendo un
número al final (_1, _2, etc.) hasta localizar un nombre de
archivo no utilizado.

[Link]
anaged_copy/8

- file_unmanaged_move($source, $destination = NULL, $replace =


FILE_EXISTS_RENAME). Permite mover un archivo desde una ubicación
a otra. Es similar a la función anterior, de forma que el archivo se copia
en el destino y, si se ha podido realizar la operación, se elimina en el
origen.

[Link]
anaged_move/8

260 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

- file_upload_max_size(). Devuelve el valor en bytes del tamaño


máximo de archivo permitido, según la configuración de PHP.

[Link]
ad_max_size/8

- file_directory_temp(). Devuelve y prepara el directorio temporal.

[Link]
tory_temp/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 261
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.3 La entidad File

Los archivos gestionados (managed files) son entidades de tipo File:

[Link]

Consulta en el apartado 30.2 las operaciones comunes a todos los tipos de


entidades, aplicables también al tipo 'file'. Solo hay que tener en cuenta que el
almacén (storage) de archivos se obtiene:

$file_storage = \Drupal::entityTypeManager()->getStorage('file');

Además de los métodos comunes al resto de entidades, la interfaz FileInterface


aporta un conjunto de métodos específicos para la gestión de archivos (ver los
métodos con prefijo File::*).

[Link]
/FileInterface/8

La entidad File utiliza como tabla base file_managed, donde almacena


directamente cierta información del archivo:

- fid. ID de la entidad archivo. Se obtiene con el método $entity->id().

- uuid. ID único.

- langcode. Idioma.

- uid. Autor del archivo. Se obtiene con el método $entity->getOwner()


(usuario) o $entity->getOwnerId() (solo el uid del usuario).

- filename. Nombre del archivo. Se obtiene con el método


$entity->getFilename().

- uri. URI completa, expresada en forma de scheme://target. Se obtiene


con el método $entity->getFileUri().

- filemime. Tipo Mime del archivo (por ejemplo, 'image/jpeg'). Se obtiene


con el método $entity->getMimeType().

- filesize. Tamaño del archivo en bytes. Se obtiene con el método


$entity->getSize().

- status. Estado. Cuando su valor es 1, indica que el archivo es


permanente.

- created. Fecha de creación. Se obtiene con el método


$entity->getCreatedTime().

- changed. Fecha de modificación. Se obtiene con el método


$entity->getChangedTime().

También están disponibles los métodos set de cada propiedad.

262 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Servicio [Link]

La tabla [Link] almacena información de uso de cada archivo:

- fid. ID del archivo.


- module. Módulo que está usando el archivo.
- type. Tipo de objeto que está usando el archivo (por ejemplo, node).
- id. Id del objeto que está usando el archivo (por ejemplo, el nid del nodo).
- count. Número de veces que ese objeto está utilizando el archivo.

El servicio file_usage, gestionado por la clase DatabaseFileUsageBackend,


permite operar sobre los usos registrados de un archivo.

[Link]
[Link]/class/DatabaseFileUsageBackend/8

Los métodos principales son:

- DatabaseFileUsageBackend::add(FileInterface $file, $module, $type, $id,


$count = 1). Registra que un módulo está usando un archivo.
- DatabaseFileUsageBackend::delete(FileInterface $file, $module, $type =
NULL, $id = NULL, $count = 1). Elimina un registro de uso de un módulo.
- DatabaseFileUsageBackend::listUsage(FileInterface $file). Indica si un
archivo está siendo utilizado por algún módulo. Dado un archivo, devuelve
un array donde cada entrada referencia al uso que hace del archivo un
módulo.

Campo de referencia a Archivo

Sabemos, por unidades anteriores, cómo obtener un campo de una entidad. En el


siguiente ejemplo accedemos al campo 'user_picture' del usuario actual.

$currentUser = \Drupal::currentUser();
$user_storage = \Drupal::entityTypeManager()->getStorage('user');
$user = $user_storage->load($currentUser->id());
$image_id = $user->get('user_picture')->first()->getValue()['target_id'];
$file_storage = \Drupal::entityTypeManager()->getStorage('file');
$image = $file_storage->load($image_id);
$image_uri = $image->getFileUri();

El método get() de la entidad devuelve un objeto de tipo FieldItemList. El método


first() devuelve el primer resultado, ya que se trata de un campo de un único valor.
Una vez que llegamos al valor del campo, estaremos trabajando con un objeto de
tipo FileItem:

[Link]
[Link]/class/FileItem/8

Obtendremos el valor del campo, y, dentro del valor devuelto, el target_id, que se
corresponde con la entidad de tipo archivo. A partir de ahí podemos cargar el
archivo y utilizar los métodos de la clase File. Por ejemplo, getFileUri() para obtener
la URI del archivo almacenado.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 263
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.4 Hooks relacionados con archivos

Los hooks relacionados con archivos solo se ejecutan cuando se realizan


operaciones sobre archivos gestionados. Por una parte se aplican los hooks
genéricos para todas las entidades (ver apartado 30.2), sustituyendo ENTITY_TYPE
por 'file':

- hook_ENTITY_TYPE_load($entities).
- hook_ENTITY_TYPE_create($entity)
- hook_ENTITY_TYPE_presave($entity).
- hook_ENTITY_TYPE_insert($entity).
- hook_ENTITY_TYPE_update($entity).
- hook_ENTITY_TYPE_predelete($entity).
- hook_ENTITY_TYPE_delete($entity).

También podemos implementar estos otros hooks específicos para trabajar con
archivos:

- hook_file_download($uri). Permite controlar el acceso a archivos


privados (ver apartado 33.6). Al intervenir cuando se va a descargar el
archivo, también se pueden añadir cabeceras adicionales al archivo
(nombre de archivo, tipo MIME, forzar descarga, etc.).

[Link]
nction/hook_file_download/8

- hook_file_mimetype_mapping_alter(&$mapping). Permite
modificar el tipo MIME del archivo.

[Link]
nction/hook_file_mimetype_mapping_alter/8

- hook_file_copy(Drupal\file\FileInterface $file, Drupal\file\FileInterface


$source). Se ejecuta cuando un archivo ha sido copiado.

[Link]
hook_file_copy/8

- hook_file_move(Drupal\file\FileInterface $file, Drupal\file\FileInterface


$source). Se ejecuta cuando un archivo ha sido movido.

[Link]
hook_file_move/8

- hook_file_url_alter(&$uri). Permite modificar la URL a un archivo. En


realidad lo que se modifica es el identificador URI.

[Link]
nction/hook_file_url_alter/8

264 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

- hook_file_validate(Drupal\file\FileInterface $file). Permite añadir


condiciones de validación sobre los archivos.

[Link]
hook_file_validate/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 265
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.5 Formularios con archivos

Drupal 8 dispone de los elementos de formulario para adjuntar archivos:

- 'file'. Elemento para subir un archivo. El archivo será gestionado o no


gestionado en función de qué función de almacenamiento utilicemos al
procesar el formulario: file_unmanaged_save_data() para un archivo
no gestionado, y file_save_upload() para un archivo gestionado.

[Link]
[Link]/class/File/8

- 'managed_file'. Elemento para subir un archivo gestionado. El elemento


valida y sube el archivo directamente, por lo que no es necesario el uso
de file_save_upload().

[Link]
[Link]/class/ManagedFile/8

Formularios con archivos no gestionados

Dentro del módulo Forcontu Files (forcontu_files), vamos a definir el formulario


Unmanaged.

Archivo: /forcontu_files/forcontu_files.[Link]
forcontu_files.unmanaged:
path: '/forcontu/files/unmanaged'
defaults:
_form: '\Drupal\forcontu_files\Form\Unmanaged'
_title: 'Unmanaged file upload'
requirements:
_permission: 'access content'

El formulario solicita un archivo de tipo PDF, y lo almacena de forma no gestionada


en 'public://unmanaged'.

Archivo: /forcontu_files/src/Form/[Link]
<?php

namespace Drupal\forcontu_files\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class Unmanaged extends FormBase {

public function buildForm(array $form, FormStateInterface $form_state) {

$form['upload'] = array(
'#type' => 'file',
'#title' => $this->t('PDF File'),
'#description' => $this->t('Upload a PDF File.'),
);

$form['actions'] = array('#type' => 'actions');

266 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Upload file'),
);

return $form;
}

public function getFormId() {


return 'forcontu_files_unmanaged';
}

public function submitForm(array &$form,


FormStateInterface $form_state) {

$directory = 'public://unmanaged';
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);

$destination = $directory . '/[Link]';

if (file_unmanaged_save_data($form_state->getValue('upload'),
$destination)) {
drupal_set_message($this->t('File Uploaded'));
} else {
drupal_set_message($this->t('Error'));
}
}
}

La función file_unmanaged_save_data() guarda el archivo subido, que


obtenemos desde $form_state->getValue('upload'), en la ubicación y con el
nombre de archivo especificos. Se trata de un archivo no gestionado, por lo que
no se almacena información en la base de datos ni se ejecutan los hooks
relacionados con archivos.

Formularios con archivos gestionados

Generalmente utilizaremos el método de archivos gestionados, usando un


elemento de formulario 'managed_file', que ya se encarga de validar el archivo y
guardarlo en la ubicación indicada.

Archivo: /forcontu_files/forcontu_files.[Link]
forcontu_files.managed:
path: '/forcontu/files/managed'
defaults:
_form: '\Drupal\forcontu_files\Form\Managed'
_title: 'Managed file upload'
requirements:
_permission: 'access content'

Archivo: /forcontu_files/src/Form/[Link]
<?php

namespace Drupal\forcontu_files\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\file\FileUsage\DatabaseFileUsageBackend;

class Managed extends FormBase {

protected $currentUser;
protected $fileUsage;

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 267
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

public function __construct(AccountInterface $current_user,


DatabaseFileUsageBackend $file_usage) {

$this->currentUser = $current_user;
$this->fileUsage = $file_usage;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('current_user'),
$container->get('[Link]')
);
}

public function buildForm(array $form, FormStateInterface $form_state) {

$form['upload'] = [
'#title' => $this->t('Upload file'),
'#type' => 'managed_file',
'#upload_location' => 'public://managed',
'#upload_validators' => [
'file_validate_extensions' => ['pdf'],
],
'#required' => TRUE,
];

$form['actions'] = array('#type' => 'actions');


$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Upload file'),
);

return $form;
}

public function getFormId() {


return 'forcontu_files_managed';
}

public function submitForm(array &$form, FormStateInterface $form_state) {

$file_storage =
\Drupal::entityTypeManager()->getStorage('file');

foreach($form_state->getValue('upload') as $fid){
$file = $file_storage->load($fid);

$this->fileUsage->add($file, 'forcontu_files', 'user',


$this->currentUser->id(), 1);
}
}
}

Cuando se sube un archivo a través de un elemento 'managed_file', este se añade


a la tabla file_managed, además de subirse a la carpeta de destino. Sin embargo,
el archivo se añade como temporal (status = 0). Para que el archivo pase a ser
permanente (status = 1), tenemos que añadirlo a la tabla file_usage, indicando
que nuestro módulo hace uso de él.

El uso del archivo se maneja con el servicio [Link], que hemos inyectado en la
clase de formulario. Como ejemplo, asociamos el archivo con nuestro módulo y con
el usuario actual.

El componente 'managed_file' acepta la propiedad ('#multiple' => TRUE), que


permite adjuntar varios archivos al formulario. En $form_state->getValue('upload'),
el valor devuelto es un array con los fid de los archivos generados.

268 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Control de permisos sobre archivos 33.6

Implementando hook_file_download(), podemos controlar el acceso a archivos


privados. Al intervenir cuando se va a descargar el archivo, también se pueden
añadir cabeceras adicionales al archivo (nombre de archivo, tipo MIME, forzar
descarga, etc.).

hook_file_download($uri)

[Link]
ok_file_download/8

La función sólo requiere como entrada el parámetro $uri, con el identificador del
archivo. La función debe devolver:

- -1, si el usuario no tiene acceso al archivo.


- Si el usuario tiene permisos, se devolverá un array con los encabezados
que se deben añadir a la descarga del archivo.
- NULL, si el módulo no actúa sobre ese archivo.

En el siguiente ejemplo se comprueba primero que el archivo esté almacenado en


la carpeta 'private://managed'. Si no está en esta carpeta, el archivo no está
gestionado por nuestro módulo y, por tanto, devolveremos NULL. Si el archivo está
en la carpeta comprobaremos si el usuario tiene el permiso 'download private
file' (permiso personalizado que tenemos que definir en el módulo Forcontu
Files. En caso afirmativo se permitirá la descarga o visualización del archivo,
devolviendo las cabeceras HTTP que se aplicarán (hemos renombrado el archivo
descargado a [Link]). Si no tiene este permiso, se prohibirá la descarga
(devuelve -1).

Archivo: /forcontu_files/forcontu_files.module
/**
* Implements hook_file_download().
*/
function forcontu_files_file_download($uri) {

$file_system = \Drupal::service('file_system');
$directory = $file_system->dirname($uri);

if(strpos($directory, 'private://managed') !== FALSE) {


if (\Drupal::currentUser()->hasPermission('download private file')) {
// tiene acceso al archivo
return [
'Content-type' => 'application/pdf',
'Content-disposition' => 'attachment; filename="[Link]"',
'Cache-Control' => 'private',
];
} else {
return -1; // no tiene acceso
}
} else {
return NULL; //no gestionado por nuestro módulo
}
}

Como se observa en este ejemplo, para evitar que los archivos privados sean
cacheados, se ha añadido la directiva 'Cache-control' => 'private', que avisa al
navegador de que no debe almacenar el archivo en cachés compartidas.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 269
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.7 Presentación de imágenes con estilos

Imagen con estilo en array renderizable

Para mostrar una imagen aplicando un estilo de imagen, utilizamos la plantilla


'image_style', indicando el estilo a aplicar y la URI del archivo:

$build['image'] = [
'#theme' => 'image_style',
'#style_name' => 'thumbnail',
'#uri' => $image_uri,
];

Otros atributos que podemos añadir a la plantilla de imagen son:

- #width y #height. Ancho y Alto de la imagen. La imagen se mostrará


con estas dimensiones, independientemente de las establecidas por el
estilo de imagen.
- #title. Valor del campo title.
- #alt. Valor del campo alt (texto alternativo).

Nota: para obtener la URI de un archivo almacenado en un campo, consulta el


apartado 33.3 (Campo de referencia a Archivo).

Enlace a imagen con estilo

Cuando lo que necesitamos es obtener el enlace de la imagen con un estilo


determinado, podemos utilizar los métodos de la entidad de configuración
'image_style':

[Link]
lass/ImageStyle/8

$style = \Drupal::entityTypeManager()->getStorage('image_style')-
>load('thumbnail');
$url = $style->buildUrl('public://[Link]');

270 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Enlace a imagen con estilo en Twig

Dentro de las plantillas Twig también podemos crear arrays renderizables.

{% set test_image = {
'#theme': 'image_style',
'#style_name': 'thumbnail',
'#uri': 'public://images/[Link],
'#alt': 'Some text',
'#attributes': { class: 'foo bar' },
} %}

{{ test_image }}

Como alternativa podemos utilizar el módulo Twig tweak, que añade nuevos
filtros y funciones de Twig:

[Link]

El nuevo filtro image_style() permite aplicar un estilo directamente a una URI o


URL:

{{ 'public://images/[Link]' | image_style('thumbnail') }}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 271
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

33.8 Efecto de imagen personalizado

Un efecto de imagen es un plugin de tipo @ImageEffect, que extiende a la clase


ImageEffectBase:

[Link]
ass/ImageEffectBase/8

Dentro de la clase del plugin debemos implementar el método applyEffect(),


encargado de realizar la transformación:

public function applyEffect(ImageInterface $image)

Como ejemplo, vamos a analizar el plugin 'image_desaturate', encargado del estilo


de imagen Desaturar:

[Link]
[Link]/class/DesaturateImageEffect/8

Archivo /core/modules/image/src/Plugin/ImageEffect/[Link]
<?php

namespace Drupal\image\Plugin\ImageEffect;

use Drupal\Core\Image\ImageInterface;
use Drupal\image\ImageEffectBase;

/**
* Desaturates (grayscale) an image resource.
*
* @ImageEffect(
* id = "image_desaturate",
* label = @Translation("Desaturate"),
* description = @Translation("Desaturate converts an image to grayscale.")
* )
*/
class DesaturateImageEffect extends ImageEffectBase {

/**
* {@inheritdoc}
*/
public function applyEffect(ImageInterface $image) {
if (!$image->desaturate()) {
$this->logger->error('Image desaturate failed using the %toolkit
toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image-
>getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image-
>getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image-
>getHeight()));
return FALSE;
}
return TRUE;
}
}

El parámetro $image implementa ImageInterface, y el plugin utiliza directamente


el método desaturate() para pasar a grises la imagen. Consulta otros métodos de
transformación disponibles en la interfaz:

[Link]
interface/ImageInterface/8

272 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 33. Archivos e imágenes

Hazlo desde la consola 33.9

Crear un plugin de efecto de imagen (Drupal Console)

drupal generate:plugin:imageeffect

Genera un plugin de efecto de imagen.

drupal generate:plugin:imageeffect options

Si no introducimos los parámetros adicionales en options, la consola los irá


solicitando de forma interactiva.

$ drupal generate:plugin:imageeffect

// Welcome to the Drupal Image Effect Plugin generator


Enter the module name [admin_toolbar]:
> forcontu_console

Enter the plugin class name [DefaultImageEffect]:


> CustomImageEffect

Enter the plugin label [Custom image effect]:


>

Enter the plugin id [custom_image_effect]:


>

Enter the plugin Description [My Image Effect]:


> Custom Image Effect

Do you confirm generation? (yes/no) [yes]:


> yes

// cache:rebuild

Rebuilding cache(s), wait a moment please.


[OK] Done clearing cache(s).
Generated or updated files

1 -
modules/custom/forcontu_console/src/Plugin/ImageEffect/[Link]

[Link]
console/content/es/commands/[Link]

Crear un plugin de formato de campo de imagen (Drupal


Console)

drupal generate:plugin:imageformatter

Genera un plugin de tipo field formatter, pero específico para un campo de imagen.

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 273
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

34 Sistema de búsqueda
El sistema de búsqueda de Drupal estará disponible al activar el
Comparativa D8/D7
módulo Search del núcleo.
En Drupal 8 las búsquedas se
implementan como un nuevo tipo de
Drupal indexa tanto los contenidos como los usuarios del sitio web, plugin.
de forma que las consultas realizadas posteriormente sean más
rápidas y eficaces. El proceso de indexación se realiza
periódicamente, a través del cron del sistema.

El motor de búsqueda no realiza la búsqueda directamente en todos


los contenidos del sitio. Lo que hacen los motores de búsqueda es
analizar las páginas y guardar la información relevante para agilizar
el proceso de búsqueda posterior. Drupal mantiene un índice de los
contenidos del sitio web y la construcción de este índice es lo que se
conoce como indexación. Cuando creamos nuevos contenidos en
el sitio web, éstos deben ser indexados (añadidos al índice de
búsquedas). De no ser así, el nuevo contenido no se mostrará en los
resultados.

Hay varias formas de interactuar con el sistema de búsquedas de


Drupal:

- Buscar en nodos.
- Buscar en otros elementos del sitio, implementando
búsquedas personalizadas.
- La búsqueda personalizada puede realizarse directamente
sobre los elementos objeto de búsqueda, o sobre el índice,
en cuyo caso tendremos que implementar la indexación de
estos elementos.

En esta unidad veremos cómo alterar las búsquedas de nodos y


cómo crear páginas de búsqueda personalizadas.

34
Contenidos de la Unidad
34.1 Introducción al sistema de búsqueda
34.2 Búsqueda de nodos
34.3 Página de búsqueda personalizada
34.4 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 277
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

34.1 Introducción al sistema de búsqueda

El módulo Search

En la Unidad 17 del curso de Experto en Drupal 8 Site Building, vimos cómo


gestionar las búsquedas desde la interfaz. La búsqueda de contenido es gestionada
por el módulo Search, que es un módulo del núcleo activado por defecto. Con el
módulo Search activado, podremos acceder a la configuración de las búsquedas
desde [F34.1a]:

URL Opciones de búsqueda Administración  Configuración  Búsqueda y meta datos 


/admin/config/search/settings Páginas de búsqueda

Lo primero que encontramos es el estado o Progreso de la indexación. El motor


de búsqueda no realiza la búsqueda directamente en todos los contenidos del sitio,
al igual que Google no busca directamente en todas las páginas web. Lo que hacen
los motores de búsqueda es analizar las páginas y guardar la información relevante
para agilizar el proceso de búsqueda posterior.

Drupal mantiene un índice de los contenidos del sitio web y la construcción de este
índice es lo que se conoce como indexación. Cuando creamos nuevos contenidos
en el sitio web, estos deben ser indexados (añadidos al índice de búsquedas).
De no ser así, el nuevo contenido no se mostrará en los resultados de las
búsquedas. Igualmente, los nodos modificados tendrán que ser reindexados
para aplicar en el índice los cambios correspondientes.

En el Estado de indexación se mostrará el porcentaje de contenidos que han


sido indexados, así como el número de elementos o contenidos que hay
actualmente pendientes de indexar. La traducción "Quedan 6 elementos en el
índice" puede resultar confusa, ya que en realidad se hace referencia a que
"quedan 6 elementos pendientes de añadir al índice".

F34.1a
Progreso de la
indexación
Estado de la indexación.

278 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Para realizar la indexación del sitio se requiere la ejecución del cron del sistema.
La indexación es una tarea que, dependiendo del volumen de contenidos del sitio,
puede consumir bastantes recursos de tiempo y memoria del servidor, por lo que
debe limitarse el número de elementos que se indexarán cada vez que se ejecuta
el cron. Este valor por defecto está fijado a 100 elementos.

Utiliza el botón Volver a indexar el sitio solo cuando necesites rehacer el índice
desde cero. Será necesario ejecutar el cron para realizar la indexación del sitio, ya
sea automática o manualmente. En un sitio en producción el cron debe ejecutarse
automática y periódicamente, para que el sitio indexe con frecuencia el contenido
nuevo creado. En un sitio en desarrollo, sin embargo, es más recomendable
trabajar con la ejecución manual del cron. Consulta los comandos de Drush y
Drupal Console en el apartado 34.5.

Una vez ejecutado el cron se habrán indexado el número de elementos definidos


en la configuración (inicialmente 100 elementos por cada ejecución de cron). En la
Figura [F17.1b] se muestra el estado de la indexación tras la ejecución del cron.
En este ejemplo, como el número de elementos en el índice era menor de 100, ha
bastado con una ejecución del cron para indexar todos los contenidos del sitio. Si
no es tu caso, ejecuta el cron tantas veces como necesite tu sitio, hasta llegar a
tener indexado el 100% del sitio.

F34.1b
Estado de indexación
Tras la ejecución del cron
los elementos ya están
indexados.

Si en este momento creamos un nuevo nodo en el sitio o modificamos uno


existente, veremos cómo se incrementa el número de elementos en el índice
(elementos pendientes de indexar) y se reduce el porcentaje total de indexación
del sitio.

Interactuar con la búsqueda desde programación

La interfaz de búsqueda (Search interface) aporta las clases, funciones y hooks


necesarios para trabajar con las búsquedas, tanto con los resultados como con la
indexación de los contenidos del sitio.

Hay dos formas de interactuar con el sistema de búsqueda:

- La búsqueda e indexación de nodos se puede alterar implementando


hook_node_update_index() y hook_node_search_result()
(apartado 34.2). Se mantiene la estructura de hooks de Drupal 7.

- Crear páginas de búsqueda personalizadas mediante la


implementación de plugins de búsqueda (apartado 34.3).

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 279
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Interfaz de búsqueda:
[Link]
ch/8

Para implementar las funciones de esta unidad crearemos el módulo Forcontu


Search (forcontu_search).

Tablas del módulo Search

El módulo Search añade las siguientes tablas:

- 'search_dataset'. Almacena los elementos que pueden ser buscados. Si


el campo 'reindex' tiene un valor distinto de 0, indica que el contenido
debe ser reindexado.

- 'search_index'. Almacena el índice de búsquedas, asociando palabras a


elementos (de search_dataset).

- 'search_total'. Almacena la "cantidad" de apariciones de cada palabra


del índice.

280 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Búsqueda de nodos 34.2

Para buscar en nodos, implementamos hook_node_update_index() y


hook_node_search_result(). Hay que tener en cuenta que, la indexación de los
nodos de cualquier tipo de contenido, incluso de los programados, se realiza
directamente, sin necesidad de ninguna implementación adicional. Toda la
información visible de un nodo es indexada, y solo tendremos que implementar
estos hooks cuando se desea indexar información no visible.

hook_node_update_index()

La función hook_node_update_index() actúa cuando el nodo está siendo


indexado por el motor de búsquedas, lo que permite añadir información para ser
indexada.

[Link]
_node_update_index/8

hook_node_update_index(\Drupal\node\NodeInterface $node)

La función recibe como parámetro el nodo que está siendo indexado ($node), y
devuelve una cadena (salida ya renderizada), que será añadida al contenido
indexado.

Veamos un ejemplo muy sencillo. En la siguiente función comprobamos si el nodo


está promocionado a la página principal. Si es así, añadimos a la indexación la
cadena "destacado".

/**
* Implements hook_node_update_index().
*/
function forcontu_search_node_update_index(\Drupal\node\NodeInterface $node) {

if($node->isPromoted()) {
return 'promocionado';
}
}

En la Figura [F34.2a] se muestra el resultado obtenido al buscar la cadena


'promocionado' antes de implementar esta funcionalidad. Como la cadena no
está incluida en ninguno de los nodos, la búsqueda no devuelve ningún resultado.

F34.2a
Búsqueda sin modificar
Resultado de mostrar la
cadena destacado sin
implementar la
funcionalidad. Como
ningún nodo contiene esta
cadena, el resultado será
vacío.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 281
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Una vez instalado el módulo y reconstruida la indexación (Volver a indexar el


sitio y ejecutar el cron), la búsqueda de la cadena 'promocionado' devolverá como
resultado los nodos que estén promocionados a la página principal (además de
aquellos que ya tuvieran en su contenido esta cadena).

F34.2b
Búsqueda de contenido
'promocionado'
Una vez implementada la
función, la búsqueda de
'promocionado' devuelve
los nodos promocionados a
la página principal, aunque
no contengan la cadena
'promocionado' en su
contenido.

Es importante entender que la cadena 'promocionado' no ha sido añadida en el


cuerpo del nodo, así que no estará visible en ningún momento, ni en los resultados
de las búsquedas ni al mostrar el nodo completo.

282 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

hook_node_search_result()

La función hook_node_search_result() actúa cuando el nodo está siendo


mostrado como resultado de una búsqueda.

[Link]
_node_search_result/8

hook_node_search_result(\Drupal\node\NodeInterface $node)

La función recibe como parámetro el nodo que está siendo mostrado en los
resultados de la búsqueda ($node), y debe devolver un array asociativo con
información adicional que será mostrada junto al resultado.

/**
* Implements hook_node_search_result().
*/
function forcontu_search_node_search_result(\Drupal\node\NodeInterface $node) {

if($node->isPromoted()) {
return ['promoted_content' => t('Promoted content')];
}

La información adicional se añadirá a la cadena que muestra el autor, la fecha de


última actualización y el número de comentarios. Hemos añadido como contenido
adicional la cadena 'Contenido promocionado', que se mostrará únicamente en los
contenidos promocionados a la página principal [F34.2c].

F34.2c
Resultado con
contenido adicional
El contenido adicional se
muestra junto al autor, la
fecha de actualización del
nodo y el número de
comentarios.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 283
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Modificación en la plantilla de resultados

También podemos acceder a la plantilla de los resultados de búsqueda (search-


[Link]). Esta plantilla se encarga de mostrar cada resultado individual.
Los nuevos valores añadidos a través de hook_node_search_result() estarán
disponibles en el array info_split (por ejemplo, info_split.promoted_content).
No olvides añadir una condición if para comprobar si la variable está disponible:

{{ title_prefix }}
<h3{{ title_attributes }}>
<a href="{{ url }}">{{ title }}</a>
{% if (info_split.promoted_content) %}
<span class="info-promoted-content">
[{{ info_split.promoted_content }}]
</span>
{% endif %}
</h3>
{{ title_suffix }}
{% if snippet %}
<p{{ content_attributes }}>{{ snippet }}</p>
{% endif %}
{% if info %}
<p>{{ info }}</p>
{% endif %}

Como ejemplo, hemos añadido el texto [Contenido destacado] junto al título


del nodo [F34.2d]. Solo se mostrará en los contenidos promocionados.

F34.2d
Plantilla modificada
Presentación de los
resultados de búsqueda
una vez modificada la
plantilla.

Para este ejemplo será necesario copiar el archivo de plantilla search-


[Link], que encontraremos en /core/modules/search/templates, en la
carpeta del tema activo. Nunca debemos modificar las plantillas del núcleo o los
módulos, igual que tampoco debemos modificar los temas del núcleo.

284 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Página de búsqueda personalizada 34.3

Podemos implementar una página de búsqueda personalizada creando un plugin


de tipo @SearchPlugin, que implemente \Drupal\search\Plugin\SearchInterface. Es
suficiente con extender una de estas dos clases base:

- \Drupal\search\Plugin\SearchPluginBase. Búsqueda simple.

[Link]
[Link]/class/SearchPluginBase/8

- \Drupal\search\Plugin\ConfigurableSearchPluginBase. Extiende a la
clase base anterior para añadir opciones de configuración a la búsqueda,
que se podrán editar desde la configuración de búsquedas en el área de
administración.

[Link]
[Link]/class/ConfigurableSearchPluginBase/8

Como ejemplo, vamos a implementar una página de búsqueda de clientes, que


buscará en los nombres de usuarios del sitio que tienen el rol Client (client).

Definimos un plugin sin configuración, extendiendo la clase SearchPluginBase. El


único método que debemos incluir en nuestra clase de forma obligatoria es el
método execute(), que realiza la búsqueda y devuelve los resultados.

El método execute() debe devolver un array con los resultados, en forma de lista.

Archivo:
/forcontu_search/src/Plugin/Search/[Link]
<?php

namespace Drupal\forcontu_search\Plugin\Search;

use Drupal\search\Plugin\SearchPluginBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Executes a seach on users with role 'client'.
*
* @SearchPlugin(
* id = "forcontu_search_clients_search",
* title = @Translation("Clients search")
* )
*/
class ForcontuSearchClientsSearch extends SearchPluginBase {

protected $database;
protected $entityManager;

static public function create(ContainerInterface $container,


array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('database'),
$container->get('[Link]'),

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 285
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

$configuration,
$plugin_id,
$plugin_definition
);
}

public function __construct(Connection $database,


EntityManagerInterface $entity_manager, array $configuration,
$plugin_id, $plugin_definition) {

$this->database = $database;
$this->entityManager = $entity_manager;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}

/**
* Execute the search.
*
* @return array
* A structured list of search results
*/
public function execute() {
$results = [];
if (!$this->isSearchExecutable()) {
return $results;
}

$keys = $this->keywords;

$query = $this->database
->select('users_field_data', 'ufd')
->extend('Drupal\Core\Database\Query\PagerSelectExtender');

$query->fields('ufd', ['uid']);
$query->condition('default_langcode', 1);
$query->condition('name', '%' . $keys . '%', 'LIKE')
->condition('status', 1);

$query->join('user__roles', 'ur', '[Link] = ur.entity_id');


$query->condition('ur.roles_target_id', 'client');

$uids = $query
->limit(3)
->execute()
->fetchCol();

$accounts = $this->entityManager->getStorage('user')->loadMultiple($uids);

foreach ($accounts as $account) {


$result = [
'title' => $account->getDisplayName() . ' ('
. $account->getEmail() . ')',
'link' => $account->url('canonical', ['absolute' => TRUE]),
];
$results[] = $result;
}

return $results;
}
}

Haciendo uso de la extensión 'PagerSelectExtender' en el select, el sistema se


encargará de paginar los resultados según el número de elementos por página
especificado con el método limit().

286 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Además del plugin, tenemos que definir la configuración inicial, que incluye la ruta.
Nótese que la ruta empezará por /search/. Nuestra página de búsqueda tendrá la
ruta /search/clients:

Archivo:
/forcontu_search/config/install/[Link].forcontu_search_clients_search.yml
langcode: en
status: true
dependencies:
module:
- forcontu_search
id: forcontu_search_clients_search
label: Clients
path: clients
weight: 1
plugin: forcontu_search_clients_search
configuration: { }

Para probar la búsqueda, crea el rol Client (client) y asígnaselo a varios usuarios.
Modifica también los nombres de los usuarios para que parte del nombre coindicida
(por ejemplo, añadiendo la cadena foo). Como hemos establecido el paginador a
3, crea al menos 4 usuarios que cumplan con las condiciones de la búsqueda.

Accede a la página /search/clients y realiza alguna búsqueda [F34.3a]:

F34.3a
Búsqueda
Resultados de la
búsqueda.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 287
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Las páginas de búsqueda se pueden configurar desde [34.3b]:

Administración  Configuración  Búsqueda y meta datos 


Páginas de búsqueda

F34.3b
Listado de páginas de
búsqueda
Páginas de búsqueda
disponibles.

Accediendo a la edición podemos configurar la página de búsqueda. Como nuestra


página no tiene configuración adicional, solo podemos modificar la etiqueta y la
ruta [34.3c].

F34.3c
Configuración de
búsqueda
Configurar la página de
búsqueda.

Desde el listado de páginas de búsqueda, podemos crear nuevas páginas de los


tipos disponibles, que internamente son instancias del tipo de plugin creado.
Esta opción es útil cuando creamos un tipo de búsqueda configurable, ya que
podemos crear varias páginas con diferentes configuraciones.

Por ejemplo, podemos crear una búsqueda de usuarios por rol, donde podamos
configurar el rol o roles a buscar. Esto nos permitiría separar en diferentes páginas
las búsquedas de usuarios registrados, editores, clientes, etc.

288 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

Otros métodos disponibles

Para los otros métodos que utiliza el plugin, disponemos de una implementación
por defecto en la clase base, así que solo los tendremos que añadir si queremos
realizar alguna modificación. Algunos de estos métodos son:

- buildResults(). Permite modificar la salida, devolviendo un array


renderizable con los resultados de la búsqueda.
- suggestedTitle(). Permite modificar el título de la página de resultados.
- getHelp(). Devuelve información de ayuda.
- searchFormAlter(). Permite modificar el formulario de búsqueda.

Búsqueda con configuración

Puedes analizar un ejemplo completo con configuración en el módulo Search Extra


Type de núcleo, incluido en la carpeta tests del módulo Search:

/core/modules/search/tests/modules/search_extra_type

Los métodos disponibles para implementar el formulario de configuración son:

- buildConfigurationForm(). En este método definiremos los campos del


formulario.
- submitConfigurationForm(). Permite almacenar los valores de
configuración desde el formulario.
- defaultConfiguration(). Permite establecer valores iniciales de
configuración.

Búsqueda con indexación

Si queremos que nuestra búsqueda indexe contenidos, tenemos que añadir al


plugin la implementación de SearchIndexingInterface.

[Link]
[Link]/interface/SearchIndexingInterface/8

Tendremos que implementar los métodos:

- indexClear(). Limpia el índice de búsquedas para este plugin.


- indexStatus(). Devuelve información sobre el estado de indexación.
- markForReindex(). Indica que el índice debe ser reindexado.
- updateIndex(). Actualiza el índice de búsquedas. Aquí es donde se
indexan los elementos que maneja nuestro plugin de búsquedas.

Como ejemplo, consulta la implementación de estos métodos en la clase


NodeSearch:

[Link]
[Link]/class/NodeSearch/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 289
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

34.4 Hazlo desde la consola

Comandos de indexación de búsquedas (Drush)

drush search-status

Indica el número total de elementos en el índice, y cuántos quedan por indexar.

drush search-status

$ drush search-status
There are 5 items out of 31 still to be indexed.

[Link]

drush search-index

Indexa los elementos pendientes de indexar.

drush search-index

drush search-index
83.87 complete. Remaining items to be indexed: 5 [ok]
The search index has been built. [ok]

[Link]

drush search-reindex

Fuerza a que se reconstruya el índice. No realiza la indexación, como podemos


comprobar ejecutando el comando drush search-status:

drush search-reindex

$ drush search-reindex
The search index must be fully rebuilt before any new items can be
indexed.
Do you really want to continue? (y/n): y
The search index will be rebuilt.
[ok]

$ drush search-status
There are 31 items out of 31 still to be indexed.

Si queremos que se vuelva a indexar el contenido, sin esperar o ejecutar el cron


del sistema, podemos usar el parámetro adicional --immediate:

drush search-reindex --immediate

290 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

$ drush search-reindex --immediate


The search index must be fully rebuilt before any new items can be indexed.
Rebuilding the index may take a long time.
Do you really want to continue? (y/n): y
0.00 complete. Remaining items to be indexed: 31 [ok]
The search index has been rebuilt. [ok]

$ drush search-status
There are 0 items out of 31 still to be indexed.

[Link]

Comandos de ejecución de cron (Drush)

drush core-cron

Ejecuta el cron del sistema.

drush core-cron
drush cron

$ drush core-cron
Cron run successful. [success]

[Link]

Comandos de ejecución de cron (Drupal Console)

drupal cron:execute

Ejecuta el cron del sistema. Si no especificamos ningún parámetro adicional, se


ejecuta la implementación de hook_cron de todos los módulos activos.

drupal cron:execute
drush cre

$ drupal cron:execute

[OK] All cron implementations requested were executed successfully

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

Si especificamos el nombre de un módulo, solo se ejecutará su implementación de


hook_cron:

drupal cron:execute module

$ drupal cron:execute search


Executing cron function of module "search"

[OK] All cron implementations requested were executed successfully

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 291
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 34. Sistema de búsqueda

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

[Link]
[Link]

drupal cron:debug

Devuelve un listado con los módulos que implementan hook_cron.

drupal cron:debug

$ drupal cron:debug

Modules implementing a cron method


----------------------------------
Module
comment
dblog
field
file
history
locale
node
search
statistics
system
update

[Link]
[Link]

drupal cron:release

Desbloquea el cron para volver a ejecutarlo.

drupal cron:release
drupal crr

$ drupal cron:release
Cron lock was released successfully

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

[Link]
[Link]

Nota: Esta opción es especialmente útil cuando el cron no finaliza correctamente y


se queda marcado como "en ejecución", lo que impide que se vuelva a ejecutar
nuevamente. Solo usaremos este comando si, al lanzar el ejecutar el cron desde la
línea de comandos (o en el registro de eventos del sistema), nos indica que el cron
no se ha podido ejecutar porque ya se está ejecutando.

292 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

35 Traducción de módulos
Todo el texto que se incluye dentro del código de los módulos, y que
Comparativa D8/D7
conocemos como texto de interfaz, debe estar preparado para ser
traducido. De esta forma podremos implementar sitios en Drupal Aunque la función t() sigue estando
completamente multilingües. No importa si nuestro sitio solo va a disponible, la utilizaremos únicamente
estar en un idioma. Si aplicamos siempre esta regla, evitaremos cuando no exista una alternativa basada
problemas futuros. en POO.
Los archivos de traducción .po y .pot son
El texto de interfaz se escribe en inglés, y luego se añade la iguales que los utilizados en Drupal 7. Las
traducción correspondiente al idioma o idiomas en que estemos herramientas que podemos utilizar para
trabajando. traducir, como el software PoEdit, también
son comunes a ambas versiones de
Drupal.
En esta unidad vamos a ver las diferentes fórmulas que existen para
que los textos de la interfaz sean traducibles.

35
Contenidos de la Unidad
35.1 Traducción de la interfaz
35.2 Archivos de traducción
35.3 Traducción de cadenas fuera del código PHP

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 295
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

35.1 Traducción de la interfaz

Todo el texto que se incluye dentro del código de los módulos, y que conocemos
como texto de interfaz, debe estar preparado para ser traducido. De esta forma
podremos implementar sitios en Drupal completamente multilingües. No importa si
nuestro sitio solo va a estar en un idioma. Si aplicamos siempre esta regla,
evitaremos problemas futuros.

El texto de interfaz se escribe en inglés, y luego se añade la traducción


correspondiente al idioma o idiomas en que estemos trabajando.

En esta unidad vamos a ver las diferentes fórmulas que existen para que los textos
de la interfaz sean traducibles.

[Link]

La función/método t()

Durante todo el curso hemos utilizado la función o método t() para la traducción
de cadenas. La función t() permite que el sistema pueda traducir las cadenas de
texto al resto de idiomas activos en el sitio.

Encontrarás una descripción detallada de la función t() en la API de Drupal:

[Link]

Como ya hemos comentado, el idioma base es el inglés, por lo que es altamente


recomendable escribir todo el código en inglés. La función t() nos permitirá añadir
posteriormente las traducciones a español (y a cualquier otro idioma) de estas
cadenas, ya sea manualmente, a través del área de traducción de la interfaz del
sitio, o automáticamente, mediante un archivo de traducción asociado al módulo.

En Drupal 8 se recomienda el uso del método $this->t() cuando esté disponible,


en lugar de t(). El método t() es proporcionado por el Trait
StringTranslationTrait:

[Link]
[Link]/trait/StringTranslationTrait/8

Siempre que incluyamos algún texto en los módulos desarrollados, debemos


hacerlo a través del método $this->t(), para permitir su posterior traducción. Si
estamos desarrollando clases propias, donde inicialmente no está disponible el
método $this->t(), añadiremos el trait StringTranslationTrait a la clase.

296 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

<?php

use Drupal\Core\StringTranslation\StringTranslationTrait;

class MyClass {

use StringTranslationTrait;

public function doSth() {


// ...
$string = $this->t('Something');
// ...
}
}

Solo usaremos la función t() directamente en programación procedimental, no


orientada a objetos (generalmente en implementaciones de hooks). Ya hemos visto
ejemplos en algunos de los hooks implementados, como hook_help().

Pase de parámetros a t()

Cuando la cadena a traducir tiene partes variables, utilizaremos variables de


sustitución dentro de la cadena, que luego se sustituirán por los valores finales
en el momento de presentación de la cadena. Por ejemplo:

$this->t('The content has been viewed %count times', ['%count' => $count]);

Si el valor de la variable $count es 5, la cadena final generada será: "The content


has been viewed 5 times" ("El contenido ha sido visto 5 veces").

Dentro de la cadena hemos incluido el nombre de la variable o cadena de


sustitución, precedido por el símbollo % (%count). Como segundo parámetro de
la función t(), hemos pasado un array que incluye cada una de esas variables, con
su correspondiente valor. En el ejemplo, el valor de '%count' se obtiene a partir de
una variable $count, cuyo valor tendrá que haber sido obtenido anteriormente.

Veamos otro ejemplo con varias cadenas de sustitución:

$context['message'] = $this->t('Synchronizing configuration: @op


@name in @collection.',
[
'@op' => $operation['op'],
'@name' => $operation['name'],
'@collection' => $operation['collection']
]);

Simplemente se integran en el texto todas las variables de sustitución, y luego se


añade la relación clave => valor en el array pasado como segundo parámetro,
donde la clave se corresponde con el nombre de la variable de sustitución.

En este segundo ejemplo, las variables de sustitución van precedidas del símbolo
@ (@op, @name, @colletion).

¿Qué diferencia hay entre usar cadenas de sustitución con % o con @? En realidad,

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 297
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

son tres los tipos de variables de sustitución posibles:

- @variable. La cadena será "limpiada" o filtrada con el método


\Drupal\Component\Utility\Html::escape(). Se realiza, por ejemplo, una
sustitución de determinados caracteres a su correspondiente entidad
HTML, para evitar que sean interpretados como código HTML:

& (ampersand) se convierte a &amp;


" (comillas dobles) se convierte a &quot;
' (comilla simple) se convierte a &#039;
< (menor que) se convierte a &lt;
> (mayor que) se convierte a &gt;

Es el formato que debemos utilizar por defecto.

- %variable. Devuelve el texto entre etiquetas <em></em> (texto


enfatizado).

- :variable. Además de la conversión proporcionada por el método


\Drupal\Component\Utility\Html::escape(), se aplica un segundo filtro de
seguridad con UrlHelper::stripDangerousProtocols(). Se utiliza cuando el
valor a sustituir es una URL.

El método t() utiliza internamente el siguiente método, donde puedes consultar


más información sobre los formatos de variables de sustitución:

[Link]
[Link]/function/FormattableMarkup::placeholderFormat/8

Otros enlaces de interés:

\Drupal\Component\Utility\Html::escape():
[Link]
tion/Html::escape/8

UrlHelper::stripDangerousProtocols()
[Link]
function/UrlHelper::stripDangerousProtocols/8

El método formatPlural()

El método formatPlural() permite formatear cadenas que hacen referencia a un


contador de elementos, con su correspondiente versión en singular y en plural. Por
ejemplo: "1 comentario", "@count comentarios".

El método formatPlural() está definido dentro del trait StringTranslationTrait, por


lo que siempre que esté disponible $this->t(), también estará disponible
$this->formatPlural().

[Link]

298 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

[Link]/function/StringTranslationTrait::formatPlural/8
En las funciones o hooks procedimentales donde no tengamos acceso directo al
trait y no podamos añadirlo, llamaremos al servicio 'string_translation' (clase
TranslationManager), de la siguiente forma:

\Drupal::translation()->formatPlural($statistics['totalcount'],
'1 view', '@count views');

[Link]
[Link]/class/TranslationManager/8

Los parámetros de la función son:

formatPlural($count, $singular, $plural, array $args = array(), array $options = array())

- $count. Variable que contiene el valor numérico del contador.

- $singular. Cadena para el valor singular (1). No utilizaremos @count


dentro de esta cadena. Por ejemplo: "1 comment" (o "one comment").

- $plural. Cadena para el plurar. Utilizaremos @count para indicar el valor


del contador. Por ejemplo: "@count comments".

- $args. Variables de sustitución adicionales. El funcionamiento es similar


al utilizado en la función t(). No es necesario incluir el valor de @count,
que será sustituido directamente por la función.

- $options. Array asociativo con opciones adicionales.

Ambas cadenas, singular y plural, estarán disponibles para su traducción desde la


interfaz.

Módulos de traducción

Los módulos del núcleo Language e Interface Translation permiten,


respectivamente, añadir idiomas adicionales y traducir los textos de la interfaz.
Para que entre en funcionamiento todo el mecanismo de traducción, estos módulos
deben estar instalados. Si hemos instalado un idioma adicional durante la
instalación del sitio (por ejemplo, el español), el sistema habrá activado los módulos
automáticamente.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 299
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

35.2 Archivos de traducción

Archivos de traducción .po

Las exportaciones e importaciones de cadenas de traducción se realizan a través


de archivos con extensión .po (Portable Object Files). Cada archivo .po se
corresponde con un idioma, por lo que el nombre del archivo será el identificador
del país o idioma: [Link] (español), [Link] (alemán), [Link] (francés), etc.

Un archivo con extensión .po está constituido por una cabecera con una serie de
metadatos y a continuación las cadenas traducidas. Cada cadena traducida está
compuesta por dos elementos:

- Un parámetro msgid que contiene la cadena original.


- Un parámetro msgstr que contiene la cadena traducida al idioma que
corresponde el archivo.

Opcionalmente, cada cadena de traducción puede tener una línea de comentario


con información del archivo de donde se obtiene la cadena.

# Spanish translation of Search API (8.x-1.0-alpha13)


# Copyright (c) 2016 by the Spanish translation team
#
msgid ""
msgstr ""
"Project-Id-Version: Search API (8.x-1.0-alpha13)\n"
"POT-Creation-Date: 2016-11-30 03:04+0000\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Language-Team: Spanish\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"

msgid "Forms"
msgstr "Formularios"
msgid "delete"
msgstr "eliminar"
msgid "title"
msgstr "título"

Los archivos .po, al igual que otros archivos del módulo, también deben estar
codificados en formato UTF-8 sin BOM.

Archivos de plantilla de traducción .pot

Los archivos .pot se utilizan como plantilla de los archivos de traducción .po. Su
estructura es similar, pero dejando las cadenas de traducción vacías:

msgid "Forms"
msgstr ""
msgid "delete"
msgstr ""
msgid "title"
msgstr ""

300 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

Traducción de módulos contribuidos

No es habitual encontrar los archivos .po incluidos en el módulo. Generalmente


descargaremos la última versión de la traducción de un módulo desde
[Link]. Sin embargo, cuando desarrollamos un módulo
personalizado que no vayamos a compartir con la comunidad, o para el que
queremos aportar la traducción desde su instalación, sí que incluiremos los
archivos .po. Veremos la ubicación exacta de estos archivos más adelante.

Drupal permite importar automáticamente las traducciones de los módulos


contribudios desde [Link]. Los archivos descargados se almacenan con
el nombre del módulo y versión en public://translations:

contact_emails-[Link]
contact_emails-[Link]
contact_storage-[Link]
contact_storage-[Link]
...

Módulo Translation template extractor (potx)

El módulo Translation template extractor (potx) añade a nuestro alojamiento


comandos de Drush que permiten extraer todas las cadenas de traducción de un
módulo, generando el archivo .pot (plantilla) o el archivo .po para un idioma
determinado.

La página en Druapl del módulo Translation template extractor (potx), se encuentra


en:

[Link]

Sin embargo, la versión de Drupal 8, aún en desarrollo, se está trabajando en:

[Link]

Aquí puedes consultar el estado actual y los problemas que se vayan encontrando
durante la migración del módulo a Drupal 8:

[Link]

Instalación del módulo potx

Sigue las instrucciones que encontrarás en la página del módulo (github). En tu


alojamiento de Forcontu puedes ejecutar estos comandos desde consola:

$ composer require kgaut/potx


$ drush en potx

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 301
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

Extraer las cadenas de traducción a un archivo .pot

Por ejemplo, para extraer las cadenas del módulo forcontu_forms:

$ drush potx single --modules=forcontu_forms --api=8

$ drush potx single --modules=forcontu_forms --api=8


Processing modules/custom/forcontu_forms/forcontu_forms.module...
Processing modules/custom/forcontu_forms/forcontu_forms.install...
Processing modules/custom/forcontu_forms/forcontu_forms.[Link]...
Processing modules/custom/forcontu_forms/forcontu_forms.[Link]...
Processing modules/custom/forcontu_forms/forcontu_forms.[Link]...
Processing modules/custom/forcontu_forms/forcontu_forms.[Link]...
Processing
modules/custom/forcontu_forms/config/install/forcontu_forms.[Link]...
Processing modules/custom/forcontu_forms/src/Form/[Link]...
Processing modules/custom/forcontu_forms/src/Form/[Link]...
Processing
modules/custom/forcontu_forms/src/Form/[Link]...
Processing modules/custom/forcontu_forms/src/Form/[Link]...

Stats
Archivos Strings Warnings
11 120 0

Hecho

En la carpeta raíz del sitio se genera un archivo llamado [Link], que


tendremos que mover y renombrar a la carpeta adecuada, como veremos más
adelante.

Añadiendo el parámetro --language, podemos extraer también las traducciones de


un idioma determinado que se hayan incorporado al sitio a través de la interfaz, o
mediante importaciones previas. El archivo generado también se llama [Link],
así que, además de moverlo, tendremos que renombrar su extensión a .po.

$ drush potx single --modules=forcontu_forms --api=8 --language=es

Extracto del archivo generado:


#: modules/custom/forcontu_forms/forcontu_forms.module:73
msgid "Highlighted"
msgstr "Destacado"

#: modules/custom/forcontu_forms/forcontu_forms.[Link]
modules/custom/forcontu_forms/forcont
u_forms.[Link]
msgid "Forcontu Forms"
msgstr ""

#: modules/custom/forcontu_forms/forcontu_forms.[Link]
msgid "Form API"
msgstr ""

#: modules/custom/forcontu_forms/src/Form/[Link]
msgid "Search"
msgstr "Buscar"

#: modules/custom/forcontu_forms/forcontu_forms.[Link]
msgid "Forcontu Forms Settings"
msgstr "Configuración de Forcontu Forms"

302 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

Editor PoEdit

Poedit es una herramienta multiplataforma que permite la edición de archivos de


traducción (.po), facilitando la traducción de cadenas. Podemos descargar la
versión correspondiente a nuestro sistema operativo desde:

[Link]

Una vez instalado, abriremos el archivo generado .po (o .pot) generado por
Translation template extractor. En la ventana principal de la aplicación se
mostrarán dos columnas, una con el texto original y otra con la traducción.
Haciendo clic sobre cada cadena podremos completar la traducción
correspondiente. Una vez añadidas las traducciones, los cambios serán
almacenados directamente en el archivo .po original, que podrá ser utilizado
directamente en la carpeta translations del módulo [F35.2a].

Si abrimos un archivo .pot, la aplicación nos pedirá primero indicar el nombre del
archivo .po que se generará a partir de la plantilla.

F35.2a
Poedit
Poedit es un software que
facilita la edición de
archivos de traducción
(.po).

Ubicación de los archivos de traducción

La ubicación de los archivos de traducción de un módulo se define mediante las


siguientes propiedades en el archivo de definición del módulo .[Link]:

- 'interface translation project'. Nombre del módulo.


- 'interface translation server pattern'. Patrón con el nombre de
archivo .po.

Por ejemplo, para el módulo forcontu_forms podríamos definir, en el archivo


forcontu_forms.[Link]:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 303
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

interface translation project: forcontu_forms


interface translation server pattern:
modules/custom/forcontu_forms/translations/%project.%[Link]

De esta forma estamos indicando que los archivos se ubicarán en la carpeta


translations del módulo y tendrán el formato de nombre:

- forcontu_forms.[Link]
- forcontu_forms.[Link]

Los patrones que podemos utilizar para indicar el patrón de ruta son:

- %core. Versión del núcleo.


- %project. Nombre del módulo.
- %version. Versión del módulo.
- %language. Código de idioma.

Más información:
[Link]
ce_translation_properties/8

Actualizar la traducción de un módulo

Podemos actualizar la traducción de un módulo utilizando el comando de Drush:

$ drush locale-update

Vacía la caché después de realizar la actualización.

Tablas de traducción

Aunque las traducciones parten de archivos .po, se incorporan a la base de datos


y es desde allí desde donde se obtienen al mostrar el sitio. Las tablas que
intervienen son:

- locales_location. Almacena la ruta de la página o archivo desde donde


se localiza la cadena a traducir.

- locales_source. Almacena la cadena original.

- locales_target. Traducción a los diferentes idiomas de la cadena


original.

304 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

Traducción de cadenas fuera del código


35.3
PHP
Ya hemos visto que en el código PHP en general, usamos el método o función t()
para traducir cadenas. Pero, ¿cómo hacemos con las cadenas que están en otros
tipos de archivo (YAML, plantillas, etc.) y que también forman parte de la interfaz?
En este apartado abordamos la traducción de esas cadenas que están fuera del
código PHP.

Traducción en archivos YAML

Muchos archivos YAML incluyen propiedades cuyos valores son parte de la interfaz
y que, por tanto, deben ser traducibles. Nosotros no tendremos que especificar
nada especial para que estos textos sean traducibles, simplemente tenemos que
localizarlos en la traducción de interfaz.

Algunos ejemplos son:

- El nombre y descripción del módulo en el archivo .[Link].


- El título (_title) de una página definida desde .[Link].
- El título y descripción de los permisos definidos en .[Link].
- Los títulos (title) definidos en .[Link], .[Link]
y .[Link].
- etc.

Cuando se generan títulos dinámicos, ya estaríamos trabajando en funciones de


callback (getTitle() o similar), y en ese caso utilizaríamos el método t() para que el
título sea traducible.

Traducción en plantillas TWIG

La función trans (o t) se utiliza para traducir cadenas desde una plantilla. Al


igual que hacemos con todos los textos incluidos directamente en el código de un
módulo, los textos que se añaden en una plantilla también deben pasar por la
función t() de traducción.

Cuando se trate de una cadena simple, sin patrones de reemplazo, usaremos el


filtro |t.

<a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home"


class="site-logo"></a>

<h2>{{ 'Book traversal links for'|t }} {{ book_title }}</h2>

<b>{{ 'Not triggered'|t }}</b>

Cuando la cadena tenga variables o patrones de reemplazo, usaremos el filtro {%


trans %}:

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 305
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

{% trans %}
Submitted by {{ author_name }} on {{ date }}
{% endtrans %}

{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}

{% trans %}
This {{ [Link] }} has a length of: {{ count }}. It contains:
{{ [Link]|placeholder }} and {{ token.bad_text }}.
{% endtrans %}

También es posible simular el funcionamiento de formatPlural() con {% plural


count %}, que aporta la cadena para el plural (> 1).

<div>
{% set count = 1 %}
{% trans %}
Found 1 item.
{% plural count %}
Found {{ count }} items.
{% endtrans %}
</div>

Por defecto, las variables son filtradas con el método escape(), que equivale a
@variable. Si queremos obtener la variable sin filtrar, podemos usar el filtro |raw:

{% trans %}
Escaped: {{ string }}
{% endtrans %}

{% trans %}
Raw: {{ string|raw }}
{% endtrans %}

Traducción en Annotations

Aunque el código de Annotations se define dento de archivos PHP, se trata de un


código de comentarios no ejecutable, por lo que tenemos que especificar de alguna
forma qué textos serán traducibles.

Sólo tenemos que utilizar la "función" @Translation() para encapsular el texto a


traducir. Cuando el sistema evalúe el contenido del annotation, estos textos
pasarán por la función t() y se incorporarán a la traducción de la interfaz.

/**
* Provides a block to display the page title.
*
* @Block(
* id = "page_title_block",
* admin_label = @Translation("Page title"),
* )
*/

306 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 35. Traducción de módulos

Traducción en archivos de configuración

Durante el curso hemos trabajado con archivos de configuración y archivos de


esquema de configuración, que definen la estructura de los primeros.

En los esquemas de configuración (.[Link]), las etiquetas (label), son


traducibles:

[Link]:
type: config_object
label: 'Webform settings'
mapping:
settings:
type: mapping
label: 'Webform default settings'
mapping:
default_page_base_path:
type: string
label: 'Default base path'
default_form_submit_label:
type: label
label: 'Default webform submit text'
default_form_submit_once:
type: boolean
label: 'Prevent duplicate submissions'
default_form_closed_message:
type: text
label: 'Default webform closed message'

Y los archivos de configuración, los valores que se hayan definido como text, string
o label, también serán traducibles:

settings:
default_page_base_path: form
default_form_closed_message: 'Sorry...This form is closed to new
submissions.'
default_form_exception_message: 'Unable to display this webform.
Please contact the site administrator.'
default_form_submit_label: Submit
default_form_submit_once: false

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 307
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

36 Formularios IV: jQuery y Ajax


JavaScript permite añadir efectos dinámicos de presentación en un
Comparativa D8/D7
sitio web. Efectos como mostrar/ocultar un elemento o presentar un
contador que se va decrementando automáticamente, sin recargar Hay muchas similitudes en la utilización de
jQuery en ambas versiones. En el uso de
la página, son posibles gracias a JavaScript. En Drupal es posible
Ajax tendremos que tener en cuenta los
añadir código JavaScript personalizado, pero también podemos cambios a nivel de POO y de enrutamiento
hacer uso de jQuery, una librería de funciones integrada en el núcleo de páginas.
que facilita el uso de JavaScript. La propiedad #ajax es muy parecida a
Drupal 7, con algunas pequeñas
JavaScript es un lenguaje que se ejecuta en el lado del cliente, pero modificaciones.
en combinación con Ajax es posible mantener una comunicación
asíncrona, en segundo plano, con el servidor. Esta comunicación con
el servidor permite realizar cambios en determinados contenidos de
una página sin necesidad de recargar la página completa.

En esta Unidad introduciremos los conceptos de jQuery y Ajax en


Drupal, estudiando su uso y su aplicación en formularios. También
veremos otras funcionalidades relacionadas, como los estados y el
autocompletado de elementos de formulario.

Contenidos de la Unidad

36
36.1 Introducción a JavaScript en Drupal
36.2 Introducción a jQuery
36.3 Librerías jQuery en el núcleo
36.4 Carga condicional de elementos de formulario (#states)
36.5 Ajax en Drupal
36.6 Ajax en formularios
36.7 Autocompletado de elementos

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 311
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

36.1 Introducción a JavaScript en Drupal

JavaScript permite añadir efectos dinámicos de presentación en un sitio web.


Efectos como mostrar/ocultar un elemento o presentar un contador que se va
decrementando automáticamente, sin recargar la página, son posibles gracias a
JavaScript.

En Drupal es posible añadir código JavaScript personalizado, pero también


podemos hacer uso de jQuery, una librería de funciones integrada en el núcleo
que facilita el uso de JavaScript.

jQuery permite manipular los elementos DOM (Document Object Model, Modelo
de Objetos del Documento), que es una API que da acceso a los objetos que
conforman las páginas HTML y XML. Manipulando estos objetos es posible
modificar el contenido, estructura y estilos de los documentos HTML y XML.
Además, también es posible actuar frente a eventos (al hacer clic o pasar el cursor
sobre un elemento, al pulsar una tecla, al cargar la página, etc.).

JavaScript es un lenguaje que se ejecuta en el lado del cliente, pero en combinación


con Ajax es posible mantener una comunicación asíncrona, en segundo plano, con
el servidor. Esta comunicación con el servidor permite realizar cambios en
determinados contenidos de una página sin necesidad de recargar la página
completa.

En esta unidad abordamos la programación JavaScript/Ajax a través de jQuery y


del framework Ajax de Drupal.

En este primer apartado nos centramos en cómo cargar un archivo con código
JavaScript. Crearemos un primero módulo llamado Forcontu jQuery
(forcontu_jquery), donde implementaremos los ejemplos de uso de jQuery.

El archivo JavaScript

En Drupal 8, todo código JavaScript debe declararse dentro de una función de


cierre (closure), de forma que se limite el ámbito de las variables utilizadas y se
evite así sobrescribir accidentalmente otras variables globales.

(function () {
'use strict';

// Custom javascript
})();

Puedes leer más sobre el modo estricto de JavaScript aquí:

[Link]

Además, siempre debe utilizarse la directiva 'use strict', de forma que se fuerza el uso
de código en modo estricto (por ejemplo, todas las variables tienen que ser declaradas.

312 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Pasando el parámetro $ a esta función podremos usar internamente la función $(),


alias de jQuery(), que es la que permite trabajar con jQuery.

(function ($) {
'use strict';

//jQuery code
})(jQuery);

Vamos a crear un primer ejemplo de código JavaScript. Veremos más sobre cómo
implementar este código jQuery en el próximo apartado. Lo que hace el script es
localizar los elementos HTML con la clase CSS .fadeout (class="fadeout"). Después
de un retardo de 2 segundos (2000), se aplicará la función fadeOut(), que irá
atenuando el elemento hasta hacerlo desaparecer del todo (en 3 segundos).

Archivo: /forcontu_jquery/js/forcontu_jquery_fadeout.js
(function ($) {
'use strict';

$(document).ready(function() {
$(".fadeout").delay(2000).fadeOut(3000);
});
})(jQuery)

Definir la librería JS

En unidades previas hemos visto cómo crear librerías con archivos CSS. Esas
mismas librerías pueden contener también archivos JS, así que el procedimiento
para añadir archivos JS es exactamente el mismo.

Los archivos JS se ubican, generalmente, en la carpeta /js del tema o del módulo,
y se declaran en el archivo .[Link].

En la Figura [F36.1a] se muestra un ejemplo de archivo .[Link], donde


se referencia al archivo css/[Link] y al archivo js/[Link]. Si el archivo Javascript
contiene jQuery, es necesario añadir la dependencia con la librería core/jquery,
ya que Drupal 8 solo carga jQuery cuando es necesario.

foo: F36.1a
version: 1.x
css: Librerías
theme: Definición de librería para
css/[Link]: {} incluir archivos CSS y JS.
js:
js/[Link]: {}
dependencies:
- core/jquery

Todas las librerías se declararán en el mismo archivo .[Link]. Una librería


puede hacer referencia a varios archivos CSS y JS, pero lo normal es que solo se
incluyan en ella los archivos que estén directamente relacionados, para cargar en
cada momento solo los scripts que realmente se vayan a utilizar.
En nuestro ejemplo creamos el archivo /js/forcontu_jquery_fadeout.js. Ahora
tenemos que definir una librería para poder referenciarlo. En el archivo .[Link]
definimos una librería llamada forcontu_jquery.fadeout, que incluye el archivo .js
anterior.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 313
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Archivo: /forcontu_jquery/forcontu_jquery.[Link]
forcontu_jquery.fadeout:
js:
js/forcontu_jquery_fadeout.js: {}
dependencies:
- core/jquery

Como ya adelantamos, Drupal 8 solo carga jQuery cuando alguna librería lo solicita.
Lo haremos añadiendo como dependencia el nombre de la librería jQuery
(core/jquery).

Nota: La librería '[Link]' se define en el archivo /core/[Link], y


usará la versión de jQuery que se haya decidido incluir en el módulo. La nueva
forma de añadir dependencias a las librerías nos permite, en caso necesario,
vincular nuestra librería a otras versiones de jQuery, previa instalación de la versión
correspondiente.

Cómo adjuntar librerías JS

Una vez definida la librería, tenemos varias formas de utilizarla. El nombre indicado
es el nombre del módulo más el nombre de la librería (no se especifica el nombre
del archivo JS directamente).

De forma global

Este método solo se puede aplicar en los temas, pero no en los módulos. En el
archivo .[Link] del tema, podemos añadir la directiva libraries para indicar las
librerías que se cargarán en todas las páginas que utilicen el tema:

name: Bartik
type: theme
base theme: classy
description: 'A flexible, ...'
package: Core
libraries:
- bartik/global-styling

En arrays y elementos renderizables

La propiedad #attached permite adjuntar librerías en elementos de arrays


renderizables (y formularios). Por ejemplo:

$build['foo'] = [
'#markup' => $this->t('Lorem ipsum...'),
'#attached' => [
'library' => [
'forcontu_jquery/forcontu_jquery.bar',
],
],
];

Dentro de una plantilla Twig

Para adjuntar una librería a una plantilla twig, en un módulo o en un tema,


utilizamos la función de twig attach_library(), indicando también el nombre del

314 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

módulo y el de la librería.

{% block messages %}
{% if message_list is not empty %}
{{ attach_library('bartik/messages') }}
<div class="messages__wrapper layout-container">
{{ parent() }}
</div>
{% endif %}
{% endblock messages %}

Función de preprocesamiento

Implementando una función de preprocesamiento (hook_preprocess_HOOK):

[Link]
tion/hook_preprocess_HOOK/8

/**
* Implements hook_preprocess_HOOK() for maintenance_page.
*/
function seven_preprocess_maintenance_page(&$variables) {
$variables['#attached']['library'][] = 'seven/maintenance-page';
}

Añadir a ciertas páginas

Si queremos limitar las páginas en las que se cargará la librería, podemos hacerlo
también a través de la función de preprocesamiento, pero a nivel de página
hook_preprocess_page(). En este caso tenemos que añadir caché según la ruta,
y comprobar la ruta actual. Por ejemplo:

/**
* Implements hook_preprocess_HOOK() for page.
*/
function forcontu_theming_preprocess_page(&$variables) {
$variables['page']['#cache']['contexts'][] = 'route';
if (\Drupal::routeMatch()->getRouteName() === 'forcontu_theming.render_elements') {
$variables['#attached']['library'][] = 'forcontu_theming/forcontu_theming.css';
}
}

Otra alternativa para añadir la librería en determinadas páginas (o condiciones), es


implementando hook_page_attachments():

[Link]
tion/hook_page_attachments/8

/**
* Implements hook_page_attachments().
*/
function foo_page_attachments(array &$attachments) {

$attachments['#attached']['library'][] = 'foo/bar';

if (!\Drupal::currentUser()->hasPermission('custom permissions')) {
$attachments['#attached']['library'][] = 'foo/baz';
}
}

Si quieres saber más sobre cómo trabajar con librerías en módulos y temas, puedes

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 315
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

consultar este enlace:

[Link]
and-javascript-js-to-a-drupal-8-module

Página /forcontu/jquery/fadeout

Volviendo a nuestro ejemplo, vamos a definir la URL (/forcontu/jquery/fadeout), y


la correspondiente clase controladora:

forcontu_jquery.fadeout:
path: '/forcontu/jquery/fadeout'
defaults:
_controller:
'\Drupal\forcontu_jquery\Controller\ForcontuJqueryController::fade
out'
_title: 'Fade out example'
requirements:
_permission: 'access content'

Dentro del método controlador, definimos un elemento de tipo 'html_tag' (<p>),


al que le añadimos la clase 'fadeout', que es la referencia que usa nuestro código
jQuery para hacer desaparecer el elemento. También añadimos la librería al array
renderizable ('#attached'):

class ForcontuJqueryController extends ControllerBase {

public function fadeout() {

$build['text'] = [
'#markup' => '<p>' . $this->t('Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Duis tristique enim lorem, quis
imperdiet ante luctus non. Phasellus sapien neque, placerat sed
odio ut, efficitur tincidunt dui.') . '</p>',
];

$build['temp_text'] = [
'#type' => 'html_tag',
'#tag' => 'p',
'#attributes' => [
'class' => 'fadeout',
],
'#value' => $this->t('This text will disappear in 5 seconds...'),
'#attached' => [
'library' => [
'forcontu_jquery/forcontu_jquery.fadeout',
],
],
];
return $build;
}
}

Ya tenemos todos los elementos en nuestro módulo para cargar la página


/forcontu/jquery/fadeout y, con ella, el archivo JS correspondiente.

Nota: Recuerda vaciar la caché del sitio cuando realices cambios en rutas, archivos
de plantillas, librerías, etc. También puede ser necesario actualizar la caché del
navegador con Control + F5.

316 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

El resultado será una página con dos párrafos. Uno de ellos tiene asignada la clase
'fadeout', y se irá desvaneciendo hasta desaparecer del todo (display: none)
[F36.1b].

F36.1b
Fade out
Efecto fade out con
jQuery.

Juntar archivos JavaScript

Si analizamos el código fuente generado en el navegador, tendríamos que localizar


la inclusión del archivo forcontu_jquery/js/forcontu_jquery_fadeout.js. Esto
dependerá de si tenemos activada o no la opción "Juntar archivos JavaScript" desde
[F36.1c]:

Administración  Configuración  Desarrollo  Rendimiento

F36.1c
Juntar archivos
JavaScript
Permite optimizar los
archivos JavaScript en
sitios en producción.

Cuando la opción está activada (recomendado en producción), se cargan unos


archivos JS optimizados (sin espacios y unidos en menos archivos):

<script
src="/sites/default/files/js/js_BKcMdIbOMdbTdLn9dkUq3KCJfIKKo2SvKo
[Link]"></script>

<!--[if lte IE 9]>

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 317
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

<script
src="/sites/default/files/js/js_VhqXmo4azheUjYC30rijnR_Dddo0WjWkF2
[Link]"></script>
<![endif]-->
<script src="/sites/default/files/js/js_LfA-
[Link]"></script>

En estas condiciones no podemos saber directamente si nuestro archivo JS se está


cargando o no. Por ello, en desarrollo se recomienda desactivar esta opción. La
versión no optmiizada muestra los archivos JavaScript independientes:

<script
src="/core/assets/vendor/domready/[Link]?v=1.0.8"></script>

<!--[if lte IE 9]>


<script
src="/core/assets/vendor/classList/[Link]?v=2014-12-
13"></script>
<![endif]-->
<script
src="/core/assets/vendor/jquery/[Link]?v=2.2.4"></script>
<script src="/core/assets/vendor/underscore/underscore-
[Link]?v=1.8.3"></script>
<script src="/core/assets/vendor/backbone/backbone-
[Link]?v=1.2.3"></script>
<script src="/core/assets/vendor/jquery-
once/[Link]?v=2.1.1"></script>
<script src="/core/misc/[Link]?v=8.3.0"></script>
<script src="/sites/default/files/languages/es_qyOYdQA-HyV-
DFayu_R3TOsO-VkczOPe_uMXT5wJmyM.js?oo04er"></script>
<script src="/core/misc/[Link]?v=8.3.0"></script>
<script src="/core/misc/[Link]?v=8.3.0"></script>
<script src="/core/assets/vendor/[Link]/ui/core-
[Link]?v=1.11.4"></script>
<script src="/core/assets/vendor/[Link]/ui/widget-
[Link]?v=1.11.4"></script>
<script
src="/core/modules/contextual/js/[Link]?v=8.3.0"></script>
<script
src="/modules/custom/forcontu_jquery/js/forcontu_jquery_fadeout.js
?oo50v8"></script>

Orden de ejecución de las librerías

Por defecto, todos los archivos JS se cargan al final del documento (en el pie). Si
necesitamos que se cargue en el encabezado, lo indicaremos en la declaración de
la librería:

js-header:
header: true
js:
[Link]: {}

js-footer:
js:
[Link]: {}

318 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

JavaScript configurable

Añadiendo la dependencia con la librería core/drupalSettings, podemos añadir


variables al código JavaScript desde PHP.

Archivo .[Link]
cuddly-slider:
version: 1.x
js:
js/[Link]: {}
dependencies:
- core/jquery
- core/drupalSettings

Desde PHP, igual que añadimos la librería con #attached, también podemos pasar
los parámetros al código JavaScript:

$build['#attached']['library'][] = 'example_module/cuddly-slider';
$build['#attached']['drupalSettings']['example_module']['cuddlySli
der']['foo'] = 'bar';

Dentro del archivo JS, se podrá acceder a la variable 'foo' de esta forma:

drupalSettings.example_module.[Link]

En este ejemplo el valor de la variable será 'bar'.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 319
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

36.2 Introducción a jQuery

En este apartado realizamos una pequeña introducción al uso de jQuery en Drupal,


que ampliaremos en el curso de Experto en Drupal 8 Front-End Development.

$(document).ready()

A través del método .ready() de jQuery el sistema comprueba que la estructura


de la página ha sido cargada y está lista para usarse, evitándose así que se haga
referencia a elementos que aún no han sido definidos.

[Link]

Como norma general utilizaremos siempre esta estructura antes de añadir el código
jQuery personalizado.

(function ($) {
'use strict';

$(document).ready(function() {
// Código jQuery
});
})(jQuery)

Selección de elementos

jQuery incluye muchas formas de hacer referencia a un elemento de la página, con


el que posteriormente interactuaremos. El listado completo de selectores está
disponible en:

[Link]

Algunos de estos selectores son:

- ID del elemento ("#id"). Selecciona el elemento con el ID indicado. En


una página el valor ID de un elemento debe ser único. Si se repitiera este
valor, el documento se considerará inválido. Independientemente de esto,
el selector devolverá el primer elemento que encuentra con el
identificador.

[Link]

En el siguiente ejemplo se buscará el elemento con id="block1",


asignándole un valor al atributo "border", a través del método .css().

Código jQuery:

$("#block1").css("border","3px solid red");

320 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Código HTML:

<div id="block1">...</div>

- Clase del elemento (".class"). Devuelve todos los elementos con la


clase indicada. Las acciones se realizarán sobre todos los elementos
encontrados.

[Link]

En el siguiente ejemplo se buscarán todos los elementos con la clase


class="block", asignándoles un valor al atributo "border", a través del
método .css().

Código jQuery:

$(".block").css("border","3px solid red");

Código HTML:

<div class="block">Block 1</div>


<div class="block">Block 2</div>

- Nombre de etiqueta ("element"). Devuelve todos los elementos con la


etiqueta indicada en "element".

[Link]

En el siguiente ejemplo se buscarán todos los elementos de tipo <div>,


independientemente de los valores de id y/o class que tengan asignados.
El elemento <span> no será devuelto.

Código jQuery:

$("div").css("border","9px solid red");

Código HTML:

<div id="block1" class="block">Block 1</div>


<div id="block2" class="block">Block 2</div>
<span>Block 3</div>

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 321
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

- Valor de un atributo (name="value"). Devuelve todos los elementos


que tengan un atributo "name" con valor "value".

[Link]

En el siguiente ejemplo se buscarán los elementos (de tipo "input") cuyo


atributo "value" tenga el valor "Monday".

Código jQuery:

$('input[value="Monday"]').parent().css("color", "red");

Código HTML:

<label>
<input type="radio" name="weekday" value="Monday" />Monday
</label>
<label>
<input type="radio" name="weekday" value="Tuesday" />Tuesday
</label>
<label>
<input type="radio" name="weekday" value="Wednesday" />Wednesday
</label>

Existen otros selectores que permiten obtener elementos en función del


atributo, pero realizando distintos tipos de comparaciones:

o [name |= "value"]. El atributo name tiene el valor "value" o


una cadena que comienza por "value-".
[Link]

o [name *= "value"]. El valor del atributo name contiene la


cadena "value" (subcadena).
[Link]

o [name ~= "value"]. El valor del atributo name contiene la


palabra "value". La palabra debe estar al principio o al final de la
cadena, y separada por espacios del resto de palabras.
[Link]

o [name != "value"]. El valor del atributo no es igual a "value".


También devuelve los elementos que no tienen definido el
atributo.
[Link]

o [name ^= "value"]. El valor del atributo comienza con la


cadena "value".
[Link]

o [name $= "value"]. El valor del atributo termina con la cadena


"value".
[Link]

322 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

- Todos los elementos ("*"). El selector "*" permite seleccionar todos los
elementos de la página.

[Link]

- Tipo de elemento. En estos selectores podemos utilizar la versión corta,


$(':button'), que equivale a $('[type=button]').

Algunos de los selectores de tipo que podemos usar son:


o :button. [Link]
o :checkbox. [Link]
o :file. [Link]
o :image. [Link]
o :password. [Link]
o :radio. [Link]
o :reset. [Link]
o :submit. [Link]
o :text. [Link]

- Orden de los elementos: pares, impares, primer elemento, último


elemento, etc. Algunos de estos selectores son:

o :even. Selecciona los elementos pares, por ejemplo, al recorrer


una tabla. Ten en cuenta que por norma general los elementos
se indexan desde 0 (0, 1, 2, 3, 4, etc.), por lo que la selección
de elementos pares (0, 2, 4, etc.) devuelve en realidad los
elementos impares (primer elemento, tercer elemento, etc.).
[Link]

o :odd. Similar a :even, pero con los elementos impares (1, 3, 5,


etc.).
[Link]

o :first. Devuelve el primer elemento.


[Link]

o :last. Devuelve el último elemento.


[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 323
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

En el siguiente ejemplo seleccionamos los elementos cuya etiqueta sea "tr" (fila),
aplicando los siguientes estilos:

- En todas las filas el color del texto será blanco (#FFFFFF).


- En las filas pares (0,2,4) el fondo será rojo.
- En las filas impares (1,3,5) el fondo será verde.
- La primera fila tendrá el fondo azul. En este caso se sobrescribe el fondo
de la fila 0.

Código jQuery:

$("tr").css("color", "#FFFFFF");
$("tr:even").css("background-color", "red");
$("tr:odd").css("background-color", "green");
$("tr:first").css("background-color", "blue");

Código HTML:

<table border="1">
<tr><td>Row #0 (first)</td></tr>
<tr><td>Row #1</td></tr>
<tr><td>Row #2</td></tr>
<tr><td>Row #3</td></tr>
<tr><td>Row #4</td></tr>
<tr><td>Row #5</td></tr>
</table>

El resultado se muestra en la Figura [F36.2a]:

F36.2a
Ejemplo jQuery
Ejemplo de selección de
elementos de una tabla en
función del orden de los
mismos (:even, :odd, :first
y :last)

324 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Modificación de CSS

Una de las acciones que podemos realizar sobre los elementos de la página es
modificar sus atributos CSS. Algunos de los métodos de la API de jQuery para
realizar estas operaciones son:

- .css(). Cuando sólo se pasa por parámetro el nombre de una propiedad


CSS, devuelve el valor de la propiedad para el primer elemento de la
selección: .css(propertyName)

var color = $("[Link]").css("background-color");

Si además del nombre de la propiedad le pasamos un valor (o conjunto


de valores), estos valores se aplicarán sobre el estilo y elemento
indicados: .css(propertyName, value) y .css(map)

$("p").css("color", "#000000");

$("#block1").css({'background-color': '#ffe', 'border':


'5px solid #ccc'});

[Link]

- .addClass(). Permite añadir una o más clases, separadas por espacio, al


atributo class="" del elemento.

[Link]

$("p").addClass("class1 class2");

- .removeClass(). Elimina una o más clases, separadas por espacio, del


atributo class="" del elemento.

[Link]

$("p").removeClass("oldclass1 oldclass2");

Es frecuente utilizar las dos funciones conjuntamente para sustituir una o


varias clases. Por ejemplo:

$("p").removeClass("oldclass1 oldclass2").addClass("class1");

Encontrarás más métodos para trabajar con CSS y atributos en la API de jQuery,
dentro de las categorías CSS y Attributes, respectivamente.

- [Link]
- [Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 325
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Eventos

A continuación, veremos algunos métodos de tipo evento. Cuando ocurre uno de


estos eventos, se llevarán a cabo las acciones indicadas.

Los eventos disponibles pueden consultarse en:

[Link]

- .change(), cuando cambia el valor de un elemento de formulario.


[Link]

- .click(), cuando se hace clic sobre el elemento.


[Link]

- .error(), cuando se devuelve un error javascript.


[Link]

- .focus(), cuando se establece el foco o elemento seleccionado. Por


ejemplo, en un formulario, el foco es el campo activo en cada momento.
[Link]

- .blur(), cuando el elemento pierde el foco.


[Link]

- .focusin, .focusout(). Equivalentes a .focus() y .blur(), respectivamente,


con la diferencia de que también se comprueban los elementos contenidos
en el elemento seleccionado.
[Link]
[Link]

- .hover(), cuando el cursor pasa por encima del elemento.


[Link]

- .keydown(), cuando el usuario presiona una tecla por primera vez. Por
ejemplo, cuando comienza a escribir dentro de un elemento de texto, el
evento se ejecuta sólo una vez, al pulsar la primera tecla.
[Link]

- .keypress(), cuando el usuario presiona una tecla. El evento se repite


con cada tecla pulsada.
[Link]

- .mousedown(), .mouseenter(), .mouseleave(), .mousemove(), .m


ouseout(), .mouseover(), .mouseup(). Estas funciones proporcionan
distintos eventos relacionados con las operaciones que podemos realizar
con el ratón (mover el cursor, arrastrar y soltar, etc.).
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]

326 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

- .scroll(), cuando se realiza scroll sobre un elemento.


[Link]

- .select(), cuando se realiza una selección de texto dentro de un


elemento.
[Link]

- .submit(), cuando se intenta enviar un formulario.


[Link]

- .toggle(). Este evento permite realizar una acción diferente cada vez que
se hace clic (comportamiento por defecto) sobre un elemento.
[Link]
En el siguiente ejemplo, cualquier elemento de lista (<li>) cambiará de
color cuando hagamos clic sobre él. Tras el primer clic cambiará a azul, si
hacemos clic nuevamente cambiará a rojo y, con un tercer clic, a verde.
A partir de ahí el ciclo se repite.

$("li").toggle(
function () {
$(this).css("color", "blue");
},
function () {
$(this).css("color", "red");
},
function () {
$(this).css("color", "green");
}
);

Destacamos también la función:

- [Link](), que evita que se ejecute la acción por defecto


para un evento determinado. Por ejemplo, llamando a esta función,
podemos evitar que, al hacer clic sobre un enlace, se cargue la URL
correspondiente.

Efectos

La API de jQuery también incluye un conjunto de métodos que añaden efectos


dinámicos en los elementos de la página. El listado completo de efectos puede
consultarse en:

[Link]

- .animate(). Permite generar animaciones modificando algunas


propiedades CSS.
[Link]

- .delay(). Añade un retardo en la animación, expresado en milisegundos.


[Link]

- .fadeIn(), .fadeOut(). Ambos efectos juegan con la opacidad del


elemento. El efecto fadeIn() hace aparecer el elemento (menor a mayor
opacidad), mientras que fadeOut() lo hace desaparecer (mayor a menor

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 327
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

opacidad).
[Link]
[Link]

- .hide(), .show(). Estos efectos permiten ocultar o mostrar un elemento.


En ambos casos se puede facilitar como parámetro el tiempo en
milisegundos que tardará el efecto, o los valores 'slow' (600 ms) y 'fast'
(200 ms).
[Link]
[Link]

- .slideDown(), .slideUp(). El efecto .slideDown() muestra el elemento


apareciendo desde arriba. El efecto .slideUp() oculta el elemento
haciéndolo desaparecer de abajo a arriba.
[Link]
[Link]

- .stop(). Para la animación que se esté ejecutando sobre el elemento.


[Link]

En el siguiente ejemplo combinamos algunos de los métodos estudiados. La capa


identificada como #box1 se mostrará u ocultará a través de los enlaces definidos
(#ocultar y #mostrar). Para ocultar la capa utilizamos el efecto .fadeOut(), que
hace desaparecer la capa reduciendo su opacidad. Para mostrar la capa utilizamos
el efecto .slideDown(), que la hará aparecer de arriba a abajo.

Código jQuery:

$(document).ready(function(){
$("#ocultar").click(function(event){
[Link]();
$('#box1').fadeOut(2000);
});

$("#mostrar").click(function(event){
[Link]();
$("#box1").slideDown(3000);
});
});

Código HTML:

<div id="box1" style="background-color: green; color:#fff;


padding:10px;">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
tellus purus, hendrerit nec porta id, commodo sed sem. Donec
scelerisque luctus arcu quis dignissim.</p>
</div>
<p>
<a href="#" id="ocultar">Ocultar la capa</a> |
<a href="#" id="mostrar">Mostrar la capa</a>
</p>

F36.2c
Efectos con jQuery
Ejemplo de efectos con
jQuery.

328 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Librerías jQuery en el núcleo 36.3

En este apartado analizamos las librerías JavaScript y jQuery incluidas en Drupal.


Drupal incluye algunas librerías de funciones propias y otros plugins que, aunque
son genéricos, vienen integrados en el núcleo. Comenzamos viendo algunos de
estos plugins genéricos.

Estas librerías pueden localizarse en el archivo /core/[Link].

Accordion

Permite formatear los elementos de una página como un acordeón, de forma que
cuando se despliega un elemento, el resto de elementos se pliegan.

La descripción completa del plugin se encuentra en:

[Link]

Declaramos la librería en el archivo .[Link]. En este caso, la dependencia


será con la librería core/[Link].

forcontu_jquery.accordion:
js:
js/forcontu_jquery_accordion.js: {}
dependencies:
- core/[Link]

Posteriormente añadimos el archivo .js, con la configuración particular que hace uso
del plugin en los elementos de la página (js/forcontu_jquery_accordion.js). El
acordeón se activará para el elemento con id="accordion".

(function ($) {
'use strict';

$(document).ready(function() {
$("#accordion").accordion();
});
})(jQuery)

Por último, definimos una página donde usaremos el efecto acordeón. Dentro del
archivo de routing hemos definido la ruta forcontu_jquery.accordion con URL
/forcontu/jquery/accordion. Como ejemplo hemos añadido un contenido
estático:

class ForcontuJqueryController extends ControllerBase {

//...
public function accordion() {

$build['content'] = [
'#markup' => '
<div id="accordion">

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 329
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

<h3><a href="#">Section 1</a></h3>


<div><p>Lorem ipsum dolor sit amet, consectetur
adipiscing elit.</p></div>
<h3><a href="#">Section 2</a></h3>
<div><p>Vivamus nec nisl vitae lorem molestie
elementum.</p></div>
<h3><a href="#">Section 3</a></h3>
<div><p>Quisque ultricies viverra hendrerit.</p></div>
</div>',
'#attached' => [
'library' => [
'forcontu_jquery/forcontu_jquery.accordion',
],
],
];

return $build;

El efecto de acordeón al hacer clic sobre los encabezados de cada sección se


muestra en la Figura [F36.3a].

F36.3a
Ejemplo: Accordion
Ejemplo de uso de la
librería Accordion. Al hacer
clic sobre uno de los títulos,
se desplegará el contenido
y se plegarán el resto de
elementos.

Otras librerías

Describimos brevemente otras librerías, que podemos utilizar en nuestro código de


la misma forma que hemos hecho en los ejemplos anteriores. En la API de jQuery
encontrarás ejemplos de uso de cada una de ellas.

- Dialog (core/[Link]). Genera una ventana flotante (o cuadro de


diálogo) con un título y un área de contenido. Esta ventana puede
moverse, ampliarse o reducirse y cerrarse.
[Link]

- Draggable (core/[Link]). Permite que un elemento sea


arrastrable con el ratón. Esta funcionalidad se utiliza habitualmente junto
con elementos que permiten soltar el elemento (plugin Droppable). Esta
técnica se conoce como Drag & Drop (arrastrar y soltar).
[Link]

- Droppable (core/[Link]). Define un elemento que permite


soltar los elementos que pueden ser arrastrados. Se utilizará, por tanto,
en conjunción con el plugin Draggable.
[Link]

330 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

- Progressbar (core/[Link]). Permite añadir una barra de


progreso, que muestra el porcentaje ejecutado de cualquier proceso. Ten
en cuenta que la barra no calcula el tiempo y porcentaje de forma
automática, sino que debemos incluir en la programación los diferentes
estados por los que va pasando la barra, en función de las tareas que se
hayan ejecutado.
[Link]

- Resizable (core/[Link]). Hace que un elemento sea


redimensionable.
[Link]

- Selectable (core/[Link]). Convierte a los elementos de una


lista en seleccionables. La selección se puede realizar directamente
haciendo clic con el ratón, utilizando Control para seleccionar elementos
no contiguos, etc.
[Link]

- Sortable (core/[Link]). Permite que los elementos sean


ordenables mediante arrastrar y soltar.
[Link]

- Tabs (core/[Link]). Genera pestañas individuales para cada


elemento.
[Link]

Efectos

En el núcleo de Drupal también se incluyen algunos efectos jQuery. Algunos de


ellos son:

- Bounce (core/[Link]). Este efecto agita el elemento


varias veces, moviéndolo horizontal y/o verticalmente según la
configuración especificada.
[Link]

- Explode (core/[Link]). Hace explotar el elemento en


varios trozos. Puede utilizarse tanto para hacer desaparecer el elemento
como para mostrarlo.
[Link]

- Fold (core/[Link]). Pliega el elemento como si se tratara


de una hoja de papel.
[Link]

- Pulsate (core/[Link]). Muestra el elemento de forma


intermitente jugando con su opacidad y el número de pulsos indicado.
[Link]

Consulta todos los efectos disponibles en el archivo /core/[Link],


buscando el prefijo [Link].*

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 331
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Traducción de cadenas

Sabemos que la función t() permite que las cadenas introducidas en el código sean
traducibles a través de la traducción de la interfaz. Pues bien, también podemos
incluir cadenas traducibles en el código JavaScript, a través de la función
Drupal.t().

En el ejemplo añadimos la cadena "Advanced search" al final del elemento #block1,


mediante el método .append(). La cadena estará disponible para ser traducida
como cualquier otra cadena añadida a través de la función Drupal.t().

$("#block1").append(Drupal.t('Advanced search'));

A la función Drupal.t() también podemos pasarle parámetros:

return Drupal.t('@count styles configured', {'@count': count});

Y, por último, mostramos un par de ejemplos de uso de la función


[Link]():

var nodesCount = [Link](count, '1 node', '@count nodes');

var nodesCountOfType = [Link](count, '1 node of


@type', '@count nodes of @type', {@type: nodeType});

332 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Carga condicional de elementos de


36.4
formulario (#states)

La propiedad #states asignada a un elemento de formulario, nos permite añadir


condiciones de comportamiento de ese elemento con respecto a otros elementos
del formulario. Por ejemplo, podemos mostrar un elemento cuando se completa
otro elemento, o abrir un contenedor de elementos al activar una opción.

Los estados se manejan desde JavaScript y solo actúan a nivel de presentación,


en el lado del cliente. Recuerda que, por accesibilidad, los formularios también
deben poder funcionar correctamente sin JavaScript activado en el navegador.

La librería que gestiona los estados en formularios es core/[Link], que


carga el archivo /core/misc/[Link]. No necesitamos cargar la librería, ya que
el sistema comprueba, a través de la clase Renderer, si el elemento a renderizar
está usando la propiedad #states, adjuntando la librería en caso necesario.

Propiedad #states

La propiedad #states es un array con esta estructura:

[
STATE1 => CONDITIONS_ARRAY1,
STATE2 => CONDITIONS_ARRAY2,

]

Cada clave (STATE1, STATE2, etc.) es un estado (visible, checked, etc.). Cada array
de condiciones tiene una estructura en forma de clave/valor, donde la clave es un
selector jQuery que hace referencia a otro elemento del formulario, al que
llamaremos elemento remoto. El valor es un array con las condiciones que debe
cumplir ese elemento remoto para que se aplique el estado al elemento principal.

[
'visible' => [
JQUERY_SELECTOR => REMOTE_CONDITIONS,
JQUERY_SELECTOR => REMOTE_CONDITIONS,
]
]

En el siguiente ejemplo, el elemento item1 solo se mostrará si el elemento


checkbox1 ha sido seleccionado (checked).

$form['item1'] = [
'#type' => 'textfield',
'#states' => [
'visible' => [
':input[name="checkbox1"]' => ['checked' => TRUE],
],
],
];

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 333
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Si un estado tiene varias condiciones, se deben cumplir todas las condiciones para
que se produzca el cambio de estado. Si queremos que solo se cumpla una de
ellas, podemos usar el operador 'or', de la siguiente forma:

$form['group'] = [
'#type' => 'container',
'#states' => [
'visible' => [
[':input[name="confirmation_type"]' => ['value' => 'page']],
'or',
[':input[name="confirmation_type"]' => ['value' => 'inline']],
],
],
];

El elemento 'group' del ejemplo anterior es un contenedor de campos que solo


estará visible si el elemento 'confirmation_type' tiene el valor 'page' o 'inline'.

El estado de un elemento contenedor afecta a todos los elementos que contiene,


de forma que se mostrarán u ocultarán de forma conjunta, sin tener que aplicar
condiciones de estado a sus elementos individuales.

Estados

Los estados que se pueden aplicar a un elemento son:

- enabled / disabled. Elemento activado o desactivado.


- required / optional. Elemento obligatorio u opcional.
- visible / invisible. Elemento visible o invisible.
- checked / unchecked. Elemento seleccionado o no seleccionado.
- expanded / collapsed. Elemento expandido o cerrado.

En las condiciones remotas, se pueden comprobar los siguientes estados:

- empty / filled. Si el elemento está vacío o rellenado.


- checked / unchecked. Si el elemento ha sido seleccionado o no.
- expanded / collapsed. Si el elemento está expandido o cerrado.
- value. Valor del elemento.

La condición value se debe usar para comprobar si una opción de una lista (select,
botones de radio, etc.) ha sido seleccionada.

Ejemplo de cambio de estado

Vamos a implementar el formulario States dentro del módulo Forcontu Forms


(forcontu_forms). Comenzamos definiendo la ruta del formulario:

Archivo forcontu_forms/forcontu_forms.[Link]
forcontu_forms.states:
path: '/forcontu/forms/states'
defaults:
_form: '\Drupal\forcontu_forms\Form\States'

334 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

_title: 'States Form'


requirements:
_permission: 'access content'

Archivo forcontu_forms/src/Form/[Link]
<?php

namespace Drupal\forcontu_forms\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class States extends FormBase {

public function buildForm(array $form, FormStateInterface


$form_state) {

$form['title'] = [
'#type' => 'textfield',
'#title' => $this->t('Title'),
'#required' => TRUE,
'#default_value' => '',
];

$form['unlock_options'] = [
'#type' => 'checkbox',
'#title' => $this->t('Check to unlock more options'),
];

$form['options'] = [
'#type' => 'container',
'#states' => [
'visible' => [
':input[name="unlock_options"]' => ['checked' => TRUE],
],
],
];

$form['options']['color'] = [
'#type' => 'select',
'#title' => $this->t('Color'),
'#options' => [
'none' => $this->t('None'),
'black' => $this->t('Black'),
'red' => $this->t('Red'),
'blue' => $this->t('Blue'),
'other' => $this->t('Other color'),
],
'#description' => $this->t('Choose a color.'),
];

$form['options']['color_name'] = [
'#type' => 'textfield',
'#size' => 50,
'#title' => $this->t('Color name'),
'#description' => $this->t('Write the color name'),
'#states' => [
'visible' => [
':input[name="color"]' => ['value' => 'other'],
],
'required' => [
':input[name="color"]' => ['value' => 'other'],
],
],
];

// ...
return $form;
}
// ...
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 335
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

El contenedor 'options' pasará a visible si se activa elemento 'unlock_options', así


que inicialmente permanece oculto [F36.4a].

F36.4a
Estados
Ejemplo de cambios de
estado en un formularo.

Al activar el check, se muestra como visible el contenedor 'options' [F36.4b].

F36.4b
Estados
Ejemplo de cambios de
estado en un formularo.

Si seleccionamos el valor 'other' en la lista de selección 'color', se mostrará el


elemento 'color_name', que además se marcará como obligatorio (required)
[F36.4c].

F36.4c
Estados
Ejemplo de cambios de
estado en un formularo.

Comprueba que, deseleccionando las opciones correspondientes, el formulario


vuelve a los estados originales.

336 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Ajax en Drupal 36.5

Mediante la tecnología Ajax podemos hacer que una página ya cargada en el


navegador siga comunicándose con el servidor de forma asíncrona, intercambiando
información en segundo plano que puede presentarse en la página sin necesidad
de recargar la página completa.

Ya hemos visto en Drupal el uso de la tecnología Ajax en algunos de los módulos


utilizados. Por ejemplo, podemos construir una vista con un listado de elementos
y paginador, utilizando Ajax. Al pasar de una página a otra el contenido de la nueva
página se obtiene mediante una llamada Ajax, realizando la consulta a la base de
datos y presentando la información sin recargar la página completa.

Drupal behaviors

Antes de empezar a trabajar con Ajax, debemos introducir el concepto de Drupal


behaviours (comportamientos).

Hasta ahora hemos utilizado el método .ready() para comprobar que la página ha
sido cargada y empezar a ejecutar nuestro código.

(function ($) {
'use strict';

$(document).ready(function() {
// Código jQuery
});
})(jQuery)

Este método puede traer consigo un problema asociado: como el código sólo se
ejecuta al cargar la página, no se aplicará a los nuevos elementos que se puedan
añadir más adelante, de forma dinámica. Este problema es especialmente relevante
cuando utilizamos Ajax, ya que es común solicitar nuevos datos al servidor.

Ejemplo sin Drupal Behaviors

Veamos un ejemplo muy sencillo. El siguiente código jQuery añade la clase CSS
"red-text" a todas las etiquetas <p>. Previamente hemos definido la clase en un
archivo .css (.red-text { color: red; }).

Todas las acciones definidas en el evento .ready() se ejecutan tan pronto esté
construida la estructura del DOM. En primer lugar, añadimos la clase a todas las
etiquetas <p>, pero esta acción sólo afectará a las etiquetas definidas en el DOM.
Por eso, cuando posteriormente añadimos código HTML adicional con el
método .append(), este contenido no recibirá la clase que sí han recibido el resto
de párrafos preexistentes.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 337
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Código jQuery:

(function($){
'use strict';
$(document).ready(function(){
//Añade la clase "red-text" a todas las etiquetas <p>
$("p").addClass("red-text");

//Añade un nuevo párrafo. Esta etiqueta <p> no recibe la nueva clase


$("#maintext").append("<p>Vivamus ut velit nisi, id ultricies
sem. Pellentesque tincidunt commodo neque et egestas.<p>");
});
})(jQuery);

Código HTML:

<div id="maintext">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
tellus purus, hendrerit nec porta id, commodo sed sem.</p>
</div>

Resultado:

Ejemplo con Drupal Behaviors

A través de Drupal behaviors podemos definir comportamientos sobre ciertos


elementos de la página, de forma que en cualquier momento puedan volver a ser
ejecutados sobre los nuevos elementos añadidos al DOM.

En el siguiente ejemplo, "encapsulamos" en [Link] la


acción que añade la clase "red-text" a las etiquetas <p>, para poder reutilizarla
cuando se añadan nuevos elementos a la página.

Ahora, después de añadir un nuevo elemento a la página, debemos llamar a la


función [Link](), que se encargará de añadirle al elemento los
comportamientos que le correspondan.

Código jQuery:

(function($){
'use strict';
[Link] = {
attach: function (context, settings) {
$("p", context).addClass("red-text");
}
};

$(document).ready(function(){
//Añade un nuevo párrafo. Esta etiqueta <p> no recibe la nueva clase
$("#maintext").append("<p>Vivamus ut velit nisi, id ultricies
sem. Pellentesque tincidunt commodo neque et egestas.<p>");

//Ejecuta los behaviors

338 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

[Link]();
});
})(jQuery);

Código HTML:

<div id="maintext">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
tellus purus, hendrerit nec porta id, commodo sed sem.</p>
</div>

La función [Link](context, settings) puede recibir los parámetros


context y settings.

El parámetro context se utiliza para pasar a la función el elemento que ha sido


añadido. Cuando no especificamos un contexto específico, la función utilizará como
contexto todo el documento (document).

En nuestro ejemplo podríamos limitar el contexto al elemento que contiene el


nuevo párrafo: [Link]($("#maintext")); De esta forma se
revisarán todas las etiquetas <p> contenidas dentro del elemento #maintext, y
sólo se añadirá la clase CSS a las etiquetas que aún no la tengan.

Cuando no especificamos en settings parámetros de configuración específicos para


el contexto actual, se utilizará el objeto global drupalSettings, que ya utilizamos
anteriormente en la definición de JavaScript configurable.

En Drupal 8, los comportamientos se definen a través de dos funciones, una cuando


el contenido se añade a la página (attach), y otra cuando se elimina (detach).

(function ($) {
'use strict';
[Link] = {
attach: function (context, settings) {
// Cuando se añade el contenido a la página
},
detach: function (context, settings) {
// Cuando se elimina el contenido de la página (opcional)
},
};

})(jQuery);

Las funciones [Link]() y [Link]() están


definidas en /core/misc/[Link].

Más sobre [Link]:

- [Link]
- [Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 339
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Uso de Ajax en Drupal

A través de Ajax podremos actualizar dinámicamente determinados elementos de


la página HTML, solicitando la información al servidor cuando se produzca algún
evento (por ejemplo, al hacer clic en un botón o enlace). La nueva información es
facilitada por el servidor en segundo plano, y actualizada en la página recargando
únicamente el elemento a modificar, sin necesidad de recargar la página completa.

La API de Ajax de Drupal aporta un conjunto de clases que permiten la gestión


completa de los eventos Ajax:

[Link]

Podemos utilizar el framework Ajax de Drupal de varias formas:

- En elementos de formulario. Utilizando la Form API podemos


incorporar eventos Ajax en los elementos de un formulario, a través de la
propiedad #ajax.

- En enlaces. Añadiendo la clase 'use-ajax' a un enlace, el enlace será


tratado como una llamada Ajax.

- En botones de formulario. Añadiendo la clase 'use-ajax-submit' en un


botón de submit de formulario, podemos ejecutar una llamada vía Ajax.

En cualquiera de estos casos, cuando el sistema recibe una llamada Ajax, el


servidor realiza las acciones solicitadas y crea un array de comandos. Estos
comandos serán convertidos a un objeto JSON, que es devuelto al cliente para su
evaluación y tratamiento.

Los comandos tienen en general una correspondencia con un método


JavaScript/jQuery. Por ejemplo, el comando "css" indicará al cliente que debe hacer
uso del método jQuery .css(). Como ya hemos estudiado, el método .css() permite
asignar estilos CSS al elemento indicado. El comando tendrá en su array de
definición los campos: nombre del comando ('command'), elemento o elementos
donde se aplica ('selector') y valores que se aplicarán ('argument').

Cada comando será convertido en un objeto de comando JavaScript, que será


aplicado en el cliente mediante los métodos definidos en [Link]. Por ejemplo,
el comando 'css' es implementado por la función definida en /core/misc/[Link]:
[Link]().

340 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Ajax en enlaces

Es posible utilizar Ajax a partir de un enlace, de forma que al hacer click sobre este,
se ejecute una función vía Ajax, que obtiene información del servidor. La
información obtenida, que en este ejemplo será la hora del servidor, se mostrará
en el elemento HTML indicado, sin recargar la página completa.

Crearemos el módulo Forcontu Ajax (forcontu_ajax), donde registramos las


siguientes rutas:

- forcontu_ajax.link, con URL 'forcontu/ajax/link'. En esta página


definimos el contenido de la página, con el enlace que hace la llamada
Ajax.

- forcontu_ajax.link_callback, con URL 'forcontu/ajax/link-callback'.


Esta ruta servirá de retorno a la llamada Ajax.

Archivo: forcontu_ajax/forcontu_ajax.[Link]
forcontu_ajax.link:
path: '/forcontu/ajax/link'
defaults:
_controller:
'\Drupal\forcontu_ajax\Controller\ForcontuAjaxController::link'
_title: 'Ajax link example'
requirements:
_permission: 'access content'

forcontu_ajax.link_callback:
path: '/forcontu/ajax/link-callback'
defaults:
_controller:
'\Drupal\forcontu_ajax\Controller\ForcontuAjaxController::linkCallback'
_title: 'Ajax link example callback'
requirements:
_permission: 'access content'

Mostramos el código completo de la clase controladora, que detallaremos a


continuación.

Archivo: forcontu_ajax/src/Controller/[Link]
<?php

namespace Drupal\forcontu_ajax\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;

class ForcontuAjaxController extends ControllerBase {

public function link() {

$build['text'] = [
'#markup' => '<p>' . $this->t('Click the link to get the
updated time from server.') . '</p>',
];

$build['time'] = [
'#type' => 'html_tag',
'#tag' => 'div',

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 341
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

'#value' => date("H:i:s"),


'#attributes' => [
'id' => 'time',
],
];

$build['refresh_time'] = [
'#title' => $this->t('Refresh time'),
'#type' => 'link',
'#url' => Url::fromRoute('forcontu_ajax.link_callback'),
'#attributes' => [
'class' => 'use-ajax',
],
];

return $build;
}

public function linkCallback() {


$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand(
'#time',
'<div id="time">' . date("H:i:s") . ' (' . $this->t("via
AJAX") . ')</div>'));
return $response;
}
}

En el método link(), que se corresponde con la ruta forcontu_ajax.link,


definimos un contenedor div con id 'time', que será sustituido vía Ajax. Fuera de
este contenedor hemos añadido un enlace con la clase 'use-ajax'. Lo verás más
claro inspeccionando el código HTML generado:

<p>Click the link to get the updated time from server.</p>


<div id="time">[Link]</div>
<a href="/forcontu/ajax/link-callback" class="use-ajax">Refresh
time</a>

En el método linkCallback() (ruta forcontu_ajax.link_callback), debemos


generar la respuesta Ajax, que es un objeto de tipo AjaxResponse:

[Link]
ss/AjaxResponse/8

El método addCommand() permite añadir comandos de Ajax, que realizarán


diferentes operaciones. Aquí hemos utilizado el comando de Ajax
ReplaceCommand, que utiliza el método replace() de jQuery. Lo que hace este
método es localizar todas las apariciones de un selector determinado (id="time"),
y sustituirlas por el código HTML de reemplazo.

[Link]
/class/ReplaceCommand/8

Al crear el objeto de tipo ReplaceCommand, hemos pasado como parámetro el


$selector (#time), y el $content, con el contenido HTML que lo reemplazará.

Nótese que no se reemplaza solo el contenido del elemento, sino el elemento


completo, incluyendo las etiquetas <div id="time"></div>, y es por ello que
volvemos a incluir las etiquetas dentro de la cadena de reemplazo.

342 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

En la Figura [F36.5a] se muestra el texto devuelto por el servidor al hacer clic


sobre el enlace.

F36.5a
Ejemplo: Ajax con
enlace
Ejemplo de uso de Ajax a
través de un enlace con la
clase 'use-ajax'.

Comandos de Ajax

Los comandos de Ajax implementan la interfaz CommandInterface, o extienden a


otros comandos que la implementan. Los comandos definidos en el núcleo se
encuentran en:

/core/lib/Drupal/Core/Ajax/*[Link]

Algunos de los comandos disponibles son:

- AppendCommand. Utiliza el método append() de jQuery para añadir


código HTML adicional al elemento indicado en el selector.

- RemoveCommand. Utiliza el método remove() de jQuery para eliminar


los elementos (y su contenido) que coinciden con el selector.

- AlertCommand. Implementa una ventana de alerta JavaScript.

- OpenDialogCommand. Abre el contenido en una ventana de diálogo


emergente.

- OpenModalDialogCommand. Es una variante del comando anterior.


La ventana modal se diferencia de la de diálogo normal, en que bloquea
la página de origen para que no se puedan realizar modificaciones hasta
que se termine de interactual con la ventana modal.

- RedirectCommand. Implementa una redirección de URL.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 343
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

36.6 Ajax en formularios

Para este apartado hemos añadido al módulo Forcontu Ajax el ejemplo AjaxDemo
incluido en fapi_example, del módulo Examples for Developers.

En nuestra implementación creamos el formulario AjaxForm, que servirá para


ilustrar un ejemplo muy típico: dos listas de selección dependientes entre sí, donde
el listado de la segunda lista dependerá de la opción seleccionada en la primera.

Nota: Aunque aquí hemos utilizado listas estáticas, definidas en la clase del
formulario, podríamos obtener esta información desde términos de taxonomía.

En este ejemplo concreto, en la primera lista se selecciona el tipo de color según


su temperatura: cálido (warm) o frío (cool). La segunda lista se recargará con los
colores del tipo seleccionado en la primera lista [F36.6a].

F36.6a
Ejemplo: Ajax en
formularios
Las listas de selección son
dependientes, de forma que
la segunda cargará un
conjunto de elementos,
obtenidos del servidor, en
función de la selección
realizada en la primera
lista.
Comenzamos creando la ruta donde mostraremos el formulario. En este caso no
será necesario registrar una ruta para realizar la llamada Ajax, como sí hacíamos
al incluir Ajax en un enlace.

Archivo: /forcontu_ajax/forcontu_ajax.[Link]
forcontu_ajax.form:
path: '/forcontu/ajax/form'
defaults:
_form: '\Drupal\forcontu_ajax\Form\AjaxForm'
_title: 'Ajax Form Example'
requirements:
_permission: 'access content'

Y definimos la clase que se encarga de gestionar el formulario.

Archivo: /forcontu_ajax/src/Form/[Link]

<?php

namespace Drupal\forcontu_ajax\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class AjaxForm extends FormBase {

private $colors = [

344 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

'warm' => [
'red' => 'Red',
'orange' => 'Orange',
'yellow' => 'Yellow',
],
'cool' => [
'blue' => 'Blue',
'purple' => 'Purple',
'green' => 'Green',
],
];

public function buildForm(array $form, FormStateInterface $form_state) {

$form['temperature'] = [
'#title' => $this->t('Temperature'),
'#type' => 'select',
'#options' => ['warm' => 'Warm', 'cool' => 'Cool'],
'#empty_option' => $this->t('-select'),
'#ajax' => [
'callback' => '::colorCallback',
'wrapper' => 'color-wrapper',
],
];

// Desactivar la caché de formulario


$form_state->setCached(FALSE);

$form['color_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'color-wrapper'],
];

$form['actions'] = [
'#type' => 'actions',
];

$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];

return $form;
}

public function getFormId() {


return 'forcontu_ajax_ajax_form';
}

public function colorCallback(array &$form, FormStateInterface $form_state) {


$temperature = $form_state->getValue('temperature');

$form['color_wrapper']['color'] = [
'#type' => 'select',
'#title' => $this->t('Color'),
'#options' => $this->colors[$temperature],
];

return $form['color_wrapper'];
}

public function submitForm(array &$form, FormStateInterface $form_state) {

}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 345
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Dentro de buildForm definimos el elemento select 'temperature', que realiza la


llamada ajax, a través de la propiedad #ajax:

$form['temperature'] = [
'#title' => $this->t('Temperature'),
'#type' => 'select',
'#options' => ['warm' => 'Warm', 'cool' => 'Cool'],
'#empty_option' => $this->t('-select'),
'#ajax' => [
'callback' => '::colorCallback',
'wrapper' => 'color-wrapper',
],
];

$form['color_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'color-wrapper'],
];

Dentro de la propiedad #ajax hemos definido los valores:

- callback. Es el método o función que realiza la llamada Ajax. Para hacer


referencia a un método de la propia clase, $this->colorCallback(), también
podríamos haberlo indicado como:
'callback' => [ $this, 'colorCallback'],

- wrapper. Identificador (id) del elemento HTML que actúa como


envoltorio del contenido Ajax.

Veremos más adelante otros atributos que podemos utilizar en la propiedad #ajax.

El método colorCallback() recibe como parámetro $form_state, lo que nos permite


obtener los valores actuales del formulario ($form_state->getValue()). Obtenemos
el listado de colores según la temperatura, y devolvemos el contenido actualizado
del elemento contenedor 'color_wrapper'.

La propiedad '#ajax'.

Ya hemos visto que un elemento de formulario puede incluir la propiedad '#ajax',


que añade un evento para establecer una comunicación Ajax con el servidor.

[Link]

La propiedad '#ajax' es un array que puede tener los siguientes valores (todos son
opcionales):

- callback. Nombre de la función o método de retorno que realiza la


llamada Ajax. El valor devuelto por esta función puede ser un contenido
HTML, un array renderizable (como en el ejemplo anterior) o un array de
comandos AJAX.

[Link]
allback

346 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

- wrapper. Define el ID del elemento HTML que se utilizará para mostrar


el contenido devuelto por la función Ajax. Es importante tener en cuenta
que se sustituye el elemento completo, y no sólo su contenido, por lo que
conviene que el nuevo contenido también incluya el valor de ID del
elemento. Generalmente utilizaremos un elemento contenedor, como
<div>, para mostrar el contenido dinámico devuelto por la función Ajax.
En el ejemplo anterior hemos utilizado un elemento de formulario de tipo
'container'.

- method. Hace referencia al método de inserción del HTML devuelto por


la petición Ajax en el envoltorio 'wrapper' definido. El valor por defecto es
'replaceWith', pero también acepta otros valores como 'after', 'append',
'before', 'prepend' o 'html', que utilizan los métodos jQuery
correspondientes.

- effect. Especifica el efecto que se utilizará al añadir el contenido desde


la petición AJAX. Por defecto el valor es 'none', y otros valores posibles
son 'fade' y 'slide'.

- speed. Si se utiliza un efecto, determina la velocidad. Puede ser 'slow',


'fast', o un valor numérico indicando el tiempo en milisegundos.

- event. Evento que dispara la petición Ajax. Se puede utilizar cualquier


evento jQuery, aunque generalmente el evento está relacionado con el
tipo de elemento de formulario. Algunos de los valores por defecto son:

o 'mousedown', para elementos submit, imagebutton y


button.
o 'blur', para elementos textfield y textarea.
o 'change', para elementos select.

En nuestro ejemplo no hemos añadido este atributo, por lo que, al ser


un elemento de tipo select, se utiliza el evento 'change'.

- prevent. El valor por defecto es 'click', y lo que persigue es evitar que se


realice un envío de formulario mientras un evento Ajax está en marcha.

- progress. Permite seleccionar el tipo de icono o barra de progreso que


se muestra mientras se espera la respuesta de la llamada Ajax.

- url. Objeto de tipo Url para enviar la petición Ajax. Generalmente no se


usa este valor, siendo suficiente con definir la función en #ajax['callback'],
sin necesidad de registrar una ruta.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 347
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

36.7 Autocompletado de elementos

Podemos añadir autocompletado a campos de tipo textfield, añadiendo la


propiedad '#autocomplete_route_name'. El valor será una ruta en la que
implementamos un controlador que devuelve un array con los resultados, en forma
de respuesta Json (JsonResponse). Veámoslo con un ejemplo.

Definimos dos rutas, una para el formulario, y otra para devolver el resultado de
autocompletado:

forcontu_ajax.form_autocomplete:
path: '/forcontu/ajax/form/autocomplete'
defaults:
_form: '\Drupal\forcontu_ajax\Form\AutocompleteForm'
_title: 'Autocomplete Form Example'
requirements:
_permission: 'access content'

forcontu_ajax.user_autocomplete:
path: '/forcontu/ajax/user_autocomplete'
defaults:
_controller:
'\Drupal\forcontu_ajax\Controller\ForcontuAjaxAutocompleteControll
er::userAutocomplete'
_title: 'User autocomplete'
requirements:
_permission: 'access content'

En el formulario añadimos un elemento de tipo textfield con la propiedad


'#autocomplete_route_name'.

<?php

namespace Drupal\forcontu_ajax\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class AutocompleteForm extends FormBase {

public function getFormId() {


return 'forcontu_ajax_autocomplete';
}

public function buildForm(array $form, FormStateInterface


$form_state) {

$form['user'] = array(
'#type' => 'textfield',
'#title' => 'Username',
'#autocomplete_route_name' => 'forcontu_ajax.user_autocomplete',
);
//...
return $form;
}

public function submitForm(array &$form, FormStateInterface


$form_state) {
//...
}
}

348 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Ahora implementamos el método que se encarga de devolver los resultados o


coincidencias en función del valor introducido por el usuario en el campo de texto,
que llega por GET a la URL de autocompletar en el parámetro 'q'.

<?php

namespace Drupal\forcontu_ajax\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ForcontuAjaxAutocompleteController extends ControllerBase {

public function userAutocomplete(Request $request) {

$string = $request->query->get('q');

$users = [ 'admin', 'foo', 'foobar', 'foobaz' ];

$matches = preg_grep("/$string/i", $users);

return new JsonResponse(array_values($matches));


}
}

Para asegurarnos de que la respuesta Json se codifica correctamente, pasamos el


array de resultados por la función array_values(), que añadirá los índices del array.

El resultado se muestra en la Figura [F36.7a].

F36.7a
Autocompletar
Elemento de tipo textfield
con autocompletar.

Las rutas también pueden parametrizarse (en .[Link]), y luego pasarle


parámetros desde el elemento de autocompletar, usando la propiedad
'#autocomplete_route_parameters'. Los parámetros se indicarán en forma de array
parámetro/valor.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 349
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 36. Formularios IV: jQuery y Ajax

Elemento entity_autocomplete

El elemento de formulario entity_autocomplete aporta la funcionalidad de


autocompletado con cualquier tipo de entidad definida.

[Link]
[Link]/class/EntityAutocomplete/8

Como ejemplo, añadimos un elemento entity_autocomplete para seleccionar nodos


(#target_type) de tipo article o page ('target_bundles'). En entidades del núcleo
podemos utilizar el #selection_handler por defecto ('default'). En entidades
personalizadas es necesario crear un plugin de selección personalizado.

$form['selected_node'] = [
'#type' => 'entity_autocomplete',
'#title' => 'Select a content',
'#target_type' => 'node',
'#selection_handler' => 'default',
'#selection_settings' => [
'target_bundles' => ['article', 'page'],
],
];

El resultado se muestra en la Figura [F36.7a]:

F36.7a
entity_autocomplete
Elemento de autocompletar
de entidades.

El elemento entity_autocomplete permite, a través de la propiedad '#autocreate',


que, si la entidad buscada no existe, se cree al enviar el formulario.

$form['tags'] = [
'#type' => 'entity_autocomplete',
'#target_type' => 'taxonomy_term',
'#autocreate' => [
'bundle' => 'tags',
],
];

Se debe especificar el bundle de la entidad y, opcionalmente, el uid si la entidad


requiere un autor. Si no se indica, la entidad se asignará al usuario actual.

350 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

37 Programación de actualizaciones
Una vez que un módulo ha sido puesto en producción o contribuido
Comparativa D8/D7
con la comunidad, nos vemos en la obligación de actualizarlo sin la
posibilidad de reinstalarlo desde cero. Esto implica que cada nueva Aunque la estructura de las funciones de
versión del módulo debe incluir el código correspondiente para que actualización son similares, la forma de
se actualice la base de datos sin afectar a los contenidos creados en acceder y modificar los elementos
el sitio web. concretos a actualizar se realiza a través
de la API y la POO de Drupal 8.

En esta unidad veremos cómo implementar funciones de


actualización para actualizar diferentes elementos que afectan al
modelo de datos del sitio.

Contenidos de la Unidad

37
37.1 Funciones de actualización
37.2 Actualización de entidades y campos
37.3 Actualización de configuración
37.4 Actualizacón de la base de datos
37.5 Hazlo desde la consola

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 353
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

37.1 Funciones de actualización

Cuando compartimos un proyecto debemos comprometernos a mantenerlo,


atendiendo a las incidencias enviadas por otros miembros de la comunidad y
publicando nuevas versiones cuando resulte necesario. Deberemos actuar de la
misma forma con cualquier módulo personalizado, no contribuido, que esté siendo
usado en un sitio en producción y que requiera algún tipo de actualización.

Al generar una nueva versión de un módulo, tenemos que tener en cuenta que ya
puede estar siendo usado por otros usuarios, o por nosotros mismos en un sitio en
producción. Esto implica que la nueva versión debe incluir el código
correspondiente para que se actualice la base de datos sin afectar a los contenidos
creados en el sitio web.

No todas las modificaciones que se realizan en un módulo requieren programar


una actualización. Debemos identificar los cambios en el modelo de datos:

- Cambios de configuración en entidades y campos. Por ejemplo, si un


campo cambia de un tipo a otro.

- Cambios en los esquemas de configuración.

- Cambios en la definición de las tablas de la base de datos.

Función hook_update_N()

La función hook_update_N() permite definir una actualización, que se ejecutará


durante el proceso de actualización del módulo cuando el sistema ejecuta
[Link]. Aquí estamos hablando siempre de actualizaciones menores, dentro
de la misma versión de Drupal (Drupal 8). Las funciones de actualización se
implementan en el archivo .install del módulo.

[Link]
unction/hook_update_N/8

En cada implementación de la función debemos especificar el valor de N, formado


por 4 dígitos con la siguiente estructura: ABCC.

- A. El primer dígito se corresponde con la versión de Drupal: 8. Cuando


lleguemos a Drupal 10 se podrán usar 2 dígitos.

- B. El segundo dígito se corresponde con la versión del módulo. Por


ejemplo, para la versión 8.x-1.* el valor es 1, y para la versión 8.x-2.* el
valor es 2.

Los módulos personalizados y contribuidos tienen su propio número de


versión independiente del núcleo, mientras que los módulos incluidos en
el núcleo tienen la misma versión del núcleo.

- CC. Secuencia, comenzando en 01, que indica el orden de las


actualizaciones.

354 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Veamos algunos ejemplos de nombre de estas funciones, aplicadas al módulo


'example_module':

function example_module_update_8101() {
//Primera actualización para la versión 8.x-1.x del módulo
}

function example_module_update_8102() {
//Segunda actualización para la versión 8.x-1.x del módulo
}

function example_module_update_8201() {
//Primera actualización para la versión 8.x-2.x del módulo
}

La ejecución de las funciones de actualización se realizará secuencialmente, a partir


de la versión del módulo instalado. Según el ejemplo anterior, si instalamos el
módulo en la versión 8.x-2.x, se aplicarán todas las funciones de actualización.

Supongamos que se lanza una nueva versión del módulo que incluye, además de
las anteriores, la siguiente función de actualización:

function example_module_update_8202() {
//Segunda actualización para la versión 8.x-2.x del módulo
}

Si actualizamos el módulo, solo se tendrán en cuenta las funciones de actualización


que no se hayan ejecutado con anterioridad (en este caso la 8202). Para que esto
sea posible, el sistema mantiene un registro de todas las funciones de actualización
que se han aplicado, por lo que las funciones ya implementadas nunca deben ser
renumeradas (ni modificadas).

La programación de funciones de actualización es muy delicada y se tienen que


tener en cuenta una serie de recomendaciones, que puedes consultar aquí:

[Link]
unction/hook_update_N/8#sec_notes

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 355
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

37.2 Actualización de entidades y campos

Ya vimos cómo sería el esqueleto de una función de actualización. En este y


próximos apartados vamos a analizar algunos ejemplos de cambios en el modelo
de datos que podemos implementar en estas funciones.

Generalmente solo implementaremos una función de actualización en cada cambio


de versión, aunque la función incluya diferentes tipos de actualización (entidades,
campos, configuración, base de datos, etc.).

Actualización de entidades y campos:


[Link]
drupal-8

Actualización de campos

La actualización de los campos puede realizarse a varios niveles:

- El cambio afecta a la definición del tipo de campo. En este caso


actuaremos sobre la entidad de configuración 'field_storage_config', que
define el almacén del campo:

[Link]
[Link]/class/FieldStorageConfig/8

En este ejemplo se cambia el tipo (type) de los campos de tipo 'foo' a


'bar'.

$field_storage_configs = \Drupal::entityTypeManager()-
>getStorage('field_storage_config')->loadByProperties(['type' =>
'foo']);

foreach ($field_storage_configs as $field_storage) {

$new_field_storage = $field_storage->toArray();
$new_field_storage['type'] = 'bar';
$new_field_storage['module'] = 'example_module';

$new_field_storage = FieldStorageConfig::create($new_field_storage);
$new_field_storage->original = $new_field_storage;
$new_field_storage->enforceIsNew(FALSE);

$new_field_storage->save();
}

- El cambio afecta a la configuración de un campo. En este caso


actuaremos sobre la entidad de configuración 'field_config', que define la
entidad Field:

[Link]
[Link]/class/FieldConfig/8

En este ejemplo cambiamos el tipo del campo 'field_tags', y establecemos


algunas opciones de configuración:

356 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

$fields = \Drupal::entityTypeManager()->getStorage('field_config')-
>loadByProperties(['field_name' => 'field_tags']);

foreach ($fields as $field) {


$new_field = $field->toArray();
$new_field['field_type'] = 'entity_reference';
$new_field['settings'] = [
'handler' => 'default:taxonomy_term',
'handler_settings' => [
'target_bundles' => [
$vocabulary_name => $vocabulary_name
],
'auto_create' => TRUE,
],
];
$new_field = FieldConfig::create($new_field);
$new_field->original = $field;
$new_field->enforceIsNew(FALSE);
$new_field->save();
}

Nota: Recuerda añadir la cláusula use correspondiente en la cabecera del


archivo .install:

use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;

Actualización de entidades

Las entidades también pueden requerir actualizaciones a diferentes niveles:

- Configuración de presentación de la entidad. Actuaremos sobre la


entidad de configuración 'entity_view_display', que contiene las opciones
de presentación de los componentes de la entidad para un modo de
presentación determinado.

[Link]
[Link]/class/EntityViewDisplay/8

En el siguiente ejemplo obtenemos la configuración de presentación de


los nodos de tipo artículo. Luego extraemos la información específica del
campo 'field_tags', utilizando el método getComponent(), y cambiamos la
configuración de 'links' (setComponent()).

$properties = [
'targetEntityType' => 'node',
'bundle' => 'article'
];

$view_displays = \Drupal::entityTypeManager()-
>getStorage('entity_view_display')->loadByProperties($properties);

foreach ($view_displays as $view_display) {


$component = $view_display->getComponent('field_tags');
$settings = [
'link' => TRUE,
];

$view_display->setComponent('field_tags', [
'settings' => $settings,
] + $component)->save();
}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 357
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

- Configuración del formulario de edición de la entidad. Actuaremos


sobre la entidad de configuración 'entity_form_display', que contiene las
opciones relacionadas con los controles de formulario de la entidad.

[Link]
[Link]/class/EntityFormDisplay/8

En el siguiente ejemplo obtenemos la entidad de configuración del


formulario de los nodos de tipo artículo. Luego cargamos el componente
del campo 'field_tags' y cambiamos la configuración de 'match_operator'.

$properties = [
'targetEntityType' => 'node',
'bundle' => 'article'
];

$form_displays = \Drupal::entityTypeManager()-
>getStorage('entity_form_display')->loadByProperties($properties);

foreach ($form_displays as $form_display) {


$component = $form_display->getComponent('field_tags');

$form_display->setComponent('field_tags', [
'settings' => [
'match_operator' => 'STARTS_WITH',
],
] + $component)->save();
}

Nota: puedes ejecutar los ejemplos anteriores directamente desde ejecutar PHP de
Devel, teniendo en cuenta:

- Comenta la parte del código que realiza el cambio de cada objeto. Aquí lo
más importante es analizar las propiedades disponibles de cada elemento.

- Añade un dpm() para analizar las variables ($field_storage, $field,


$component, etc.) y ver qué propiedades hay disponibles antes de realizar
modificaciones sobre cada objeto.

- Si realizas modificaciones sobre un elemento, vuelve a obtenerlo para


comprobar que la configuración se ha modificado correctamente.

358 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Actualización de configuración 37.3

Cuando en la actualización se modifican archivos de configuración, seguiremos


estos pasos:

1. Actualizar los archivos de esquema de configuración. Archivos .yml


en /config/schema. De esta forma nos aseguramos de que las nuevas
instalaciones del módulo usarán los esquemas de configuración
actualizados.

2. Actualizar los archivos de configuración. Archivos .yml en


/config/install y /config/optional. De esta forma nos aseguramos de
que las nuevas instalaciones del módulo usarán los valores de
configuración actualizados.

3. En la función de actualización hook_update_N(), implementaremos la


actualización de la configuración para los casos en que el módulo ya había
sido instalado en una versión anterior.

Actualización de configuración:
[Link]
drupal-8

Para realizar modificaciones en la configuración, partimos de la factoría de


configuración (ConfigFactory), para llegar al objeto de configuración (Config):

[Link]
ass/ConfigFactory/8

[Link]
nfig/8

Para guardar el objeto de configuración (una vez realizados los cambios), usamos
el método:

Config::save($has_trusted_data = FALSE)

Pues bien, tenemos que asegurarnos de pasar el parámetro $has_trusted_data


a TRUE, para que el método no compruebe el esquema de configuración. Esto
debe hacerse así porque no podemos asegurar en este punto que el esquema de
configuración en el módulo se corresponda con la configuración que se está
realizando en esta función de actualización, ya que puede ser una actualización
intermedia. Por ejemplo:

$config->save(TRUE);

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 359
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Veamos un ejemplo muy sencillo, donde se modifica el objeto de configuración


'[Link]', añadiendo la variable adicional 'fruit'.

Archivo de configuración previo:


pets:
- dog
- cat

Archivo de configuración actualizado:


pets:
- dog
- cat
fruit:
- apple
- banana
- mango

Función de actualización:
function example_update_8001() {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('[Link]');
$config->set('fruit', ['apple', 'banana', 'mango']);
$config->save(TRUE);
}

Consulta otros ejemplos de actualización de configuración en:

[Link]

Nota: Localiza otros ejemplos en los módulos del núcleo buscando las llamadas a

\Drupal::configFactory()->getEditable(

en los archivos *.install.

Por ejemplo, en el archivo /core/system/[Link] encontramos la siguiente


actualización de un objeto de configuración. En este caso se elimina la
configuración.

/**
* Removes the [Link] configuration.
*/
function system_update_8002() {
\Drupal::configFactory()->getEditable('[Link]')->delete();
return t('The [Link] configuration has been moved to a
container parameter, see [Link] for more
information.');
}

360 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Actualización de la base de datos 37.4

Por último, vamos a ver cómo implementar actualizaciones cuando se han hecho
cambios en el esquema de la base de datos. Seguiremos estos pasos:

1. Actualizar hook_schema() con los cambios. De esta forma nos


aseguramos de que las nuevas instalaciones del módulo creen las tablas
con la estructura actualizada.

2. Implementamos la función de actualización hook_update_N() para


realizar los cambios en los casos en que el módulo ya había sido instalado
en una versión anterior.

Actualización del esquema de la base de datos:


[Link]
data-in-drupal-8

En la clase Schema puedes consultar todos los métodos disponibles para manipular
las tablas y campos de la base de datos (Apartado 20.5):

[Link]
s/Schema/8

Añadir una nueva columna a una tabla

El método Schema::addField() permite añadir un campo a una tabla. En el siguiente


ejemplo, extraído del módulo system, se añade el campo 'title' a la tabla
'menu_tree'.

function system_update_8001(&$sandbox = NULL) {

$database = \Drupal::database();
$schema = $database->schema();

$spec = [
'description' => 'The title for the link.',
'type' => 'blob',
'size' => 'big',
'not null' => FALSE,
'serialize' => TRUE,
];
$schema->addField('menu_tree', 'title', $spec);
}

También podemos usar otros métodos relacionados:

- Schema:changeField(). Modificar un campo de una tabla.


- Schema:dropField(). Eliminar un campo de una tabla.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 361
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Añadir una nueva tabla

El método Schema::createTable() permite crear una tabla nueva, según la


especificación facilitada. En el siguiente ejemplo creamos la tabla foo con los
campos bar y baz:

$database = \Drupal::database();
$schema = $database->schema();

$spec = [
'description' => 'Description',
'fields' => [
'bar' => [
'description' => 'Bar field description.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'baz' => [
'description' => 'Baz field description',
'type' => 'text',
'not null' => TRUE,
],
],
'primary key' => ['bar'],
];

$schema->createTable('foo', $spec);

Añadir claves primarias o índices

También disponemos de métodos para añadir, editar y eliminar claves primarias e


índices. Por ejemplo:

$database = \Drupal::database();
$schema = $database->schema();

$spec = ['bar'];

// Add bar as a normal index.


$schema->addIndex('mytable', $spec);

// Add bar as a primary key.


$schema->addPrimaryKey('mytable', $spec);

Actualizar datos en una tabla

Dentro de la función de actualización también podemos lanzar consultas para


modificar los datos de cualquier tabla (Unidad 21).

362 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Hazlo desde la consola 37.5

Comandos de actualización de módulos (Drush)

drush updatedb-status

Lista las actualizaciones de la base de datos pendientes de aplicar.

drush updatedb-status
drush updbst

$ drush updatedb-status
Module Update ID Description
Forcontu_entities 8001 8001 -

[Link]

drush updatedb

Equivale a lanzar [Link], aplicando las actualizaciones de la base de datos


pendientes.

drush updatedb
drush updb

$ drush updatedb
The following updates are pending:

forcontu_entities module :
8001 -

Do you wish to run all pending updates? (y/n): y


Performing forcontu_entities_update_8001 [ok]
Cache rebuild complete. [ok]
Finished performing updates. [ok]

[Link]

Añadiendo la opción --entity-updates para actualizar los cambios en configuración


de entidades. Ya lo vimos en funcionamiento al implementar nuevas entidades.

drush updatedb --entity-updates

$ drush updatedb --entity-updates


The following updates are pending:

forcontu_entities_message entity type :


El tipo de entidad Mensaje necesita ser instalado.
forcontu_entities_section entity type :
El tipo de entidad Section necesita ser instalado.
forcontu_entities_message_type entity type :
El tipo de entidad Message type necesita ser instalado.
Do you wish to run all pending updates? (y/n): y

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 363
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

Cache rebuild complete. [ok]


Finished performing updates. [ok]

También se puede utilizar el comando:

drush entity-updates

[Link]

Comandos de actualización de módulos (Drupal Console)

drupal update:debug

Lista las actualizaciones de la base de datos pendientes de aplicar.

drupal update:debug
drupal upd

$ drupal update:debug

Modules with pending updates


------------------- ---------- -------------
Module Update N Description
------------------- ---------- -------------
forcontu_entities 8002
forcontu_entities 8003
------------------- ---------- -------------

Modules with pending post updates


-------- ---------------------- -------------
Module Post update function Description
-------- ---------------------- -------------

[Link]
[Link]

drupal module:update

Permite actualizar el núcleo, un módulo o varios módulos.

drupal module:update module --composer

Si no se indica un módulo, se actualizarán el núcleo y los módulos, si están


gestionados por Composer. Si indicamos un módulo o varios módulos separados
por espacio, se actualizarán solo esos módulos.

[Link]
[Link]

364 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

drupal update:execute

Ejecuta una función específica de actualización dentro de un módulo, o todas si no


se especifica un número.

drupal update:execute module update-n


drupal upe

$ drupal update:execute forcontu_entities 8002


Switch site into maintenance mode
Executing update function "8002" of module "forcontu_entities"
Operating in maintenance mode off

// cache:rebuild
Rebuilding cache(s), wait a moment please.
[OK] Done clearing cache(s).

[Link]
[Link]

Implementar una función de actualización (Drupal Console)

drupal generate:update

Genera una implementación de hook_update_N().

drupal generate:update

Si no se especifican las opciones adicionales, se solicitarán por consola.

$ drupal generate:update
Enter the module name [admin_toolbar]:
> forcontu_console

Please provide the Update N to be implemeted [8001]:


> 8001

Do you confirm generation? (yes/no) [yes]:


> yes

// cache:rebuild

Rebuilding cache(s), wait a moment please.

[OK] Done clearing cache(s).

Generated or updated files

1 - modules/custom/forcontu_console/forcontu_console.install

[Link]
console/content/es/commands/[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 365
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 37. Programación de actualizaciones

En el archivo .install del módulo se crea el esqueleto de la función:

<?php
/**
* Implements hook_update_N() on Module forcontu_console Update # 8001.
*/
function forcontu_console_update_8001(&$sandbox) {
drupal_set_message('Module forcontu_console Update # 8001 () was
executed successfully.');
}

366 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

38 Tests automáticos
Los tests automáticos, también denominados pruebas de regresión,
Comparativa D8/D7
se utilizan para comprobar, después de añadir o modificar una
funcionalidad, que el cambio introducido no rompe ninguna Simpletest es un framework propio de
funcionalidad existente con anterioridad. Drupal, también incluido en versiones
anteriores. PHPUnit es un framework
externo a Drupal, así que se puede utilizar
Podemos encontrar dos tipos de tests automáticos:
con cualquier versión, aunque es en
Drupal 8 cuando se ha incorporado a la
- Tests unitarios. Realizan comprobaciones a nivel de clases, distribución (como componente externo).
para asegurar que sus métodos funcionan correctamente.

- Tests funcionales. Realizan comprobaciones de


funcionalidad a un nivel más alto, simulando la carga del
sitio en el navegador y verificando, generalmente, que la
salida es la esperada.

En esta unidad veremos una introducción a los dos frameworks


disponibles en Drupal: PHPUnit, que es una librería externa, y
Simpletest, que es un framework propio.

38
Contenidos de la Unidad

38.1 Qué son los tests automáticos


38.2 Ejecución de tests
38.3 Tests unitarios
38.4 Tests funcionales con SimpleTest
38.5 Tests funcionales con PHPUnit

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 369
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

38.1 Qué son los tests automáticos

Los tests automáticos, también denominados pruebas de regresión, se


utilizan para comprobar, después de añadir o modificar una funcionalidad, que el
cambio introducido no rompe ninguna funcionalidad existente con anterioridad.

Podemos encontrar dos tipos de tests automáticos:

- Tests unitarios. Realizan comprobaciones a nivel de clases, para


asegurar que sus métodos funcionan correctamente.

- Tests funcionales. Realizan comprobaciones de funcionalidad a un nivel


más alto, simulando la carga del sitio en el navegador y verificando,
generalmente, que la salida es la esperada.

Drupal cuenta con dos frameworks diferentes para programar tests automáticos,
PHPUnit, que es una librería externa, y un framework propio denominado
Simpletest.

- Los tests unitarios sobre clases que no requieren la carga del entorno
Drupal, los programaremos en PHPUnit.

- Los tests funcionales que dependen de la carga de Drupal (base de


datos y configuración) o de la salida al navegador, los programaremos en
Simpletest. Como veremos, también existe una variante de PHPUnit
para programar tests funcionales.

Enlaces relacionados:

Automated tests:
[Link]

PHPUnit in Drupal 8:
[Link]

370 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Ejecución de tests 38.2

Como veremos en próximos apartados, los tests se programan como clases


independientes en unas carpetas específicas del módulo, y no se hace uso de ellos
durante el funcionamiento normal del módulo.

Para que los tests sean ejecutables, tenemos que activar el módulo Testing del
núcleo. Una vez activado, podremos ejecutar los tests desde la interfaz de
administración o desde la consola. Veremos ambos métodos en este apartado.

Módulo Testing

El módulo Testing incluido en el núcleo permite ejecutar los tests, tanto desde la
interfaz como desde consola. Una vez instalado, podemos acceder a la
configuración del módulo desde:

Administración  Configuración  Desarrollo  Testing (Probando)

Se mostrará un listado completo con los módulos y componentes que implementan


algún test [F38.2a].

F38.2a
Testing
Ejecución de tests desde la
interfaz con el módulo
Testing.

Seleccina el módulo a probar y haz clic en Ejecutar pruebas. Cada prueba puede
tener varios pasos, que serán comprobados uno a uno, mostrándose el resultado
detallado del test [F38.2b].

El módulo Testing permite ejecutar todos los tipos de tests definidos, tanto de
Simpletest como de PHPUnit.

Nota: Para el correcto funcionamiento de los tests de PHPUnit, el módulo xdebug


de PHP debe estar desactivado a nivel de Apache.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 371
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

F38.2b
Testing
Resultado detallado de los
tests ejecutados.

Ejecución desde consola

Desde el directorio raíz del sitio, ejecuta el comando:

$ php core/scripts/[Link]

$ php core/scripts/[Link] --class


'Drupal\Tests\block\Kernel\BlockStorageUnitTest'
Drupal test run
---------------
Tests to be run:
- Drupal\Tests\block\Kernel\BlockStorageUnitTest
Test run started:
Tuesday, April 11, 2017 - 23:49

Test summary
------------
Drupal\Tests\block\Kernel\BlockStorageUnitTest 2 passes
Test run duration: 6 sec

Las opciones disponibles se pueden consultar en:

[Link]

También ejecutando el comando con --help

$ php core/scripts/[Link] --help

Aunque el comando anterior ejecuta todos los tipos de tests, también podemos
usar el comando específico de PHPUnit:

[Link]

372 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Tests unitarios 38.3

Los tests unitarios, que programaremos con PHPUnit, se ubican en el espacio de


nombres Drupal\Tests\mymodule\Unit, dentro de la carpeta del módulo:

mymodule/tests/src/Unit

Un test unitario se implementa con una clase que extiende a UnitTestCase:

[Link]
UnitTestCase/8

El nombre de la clase describirá la prueba a realizar y usará el sufijo Test. Por


ejemplo:

- /mymodule/tests/src/Unit/[Link]

Generalmente, el nombre usado para la clase Test se corresponde con el nombre


de la clase a testear, más el sufijo Test.

La clase de test incluye un bloque de Annotations con diferentes directivas que


aportan información sobre el test.

Dentro del mismo Test, cada caso o prueba a realizar, se programará en un método
independiente, sin argumentos, cuyo nombre empieza por test. Por ejempo:
testUserBlock().

La clase UnitTestCase extiende a PHPUnit_Framework_TestCase, que es una


librería externa ubicada en:

/vendor/phpunit/phpunit/src/Framework/[Link]

Al tratarse de una librería externa, no está disponible en la API de Drupal, pero


puedes consultar todos los métodos disponibles en la documentación oficial de
PHPUnit:

[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 373
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Ejemplo: JsonTest

Como primer ejemplo, analizaremos el test JsonTest, que evalúa el correcto


funcionamiento de la clase Drupal\Component\Serialization\Json, ambos definidos
en el núcleo.

Clase a testear: /core/lib/Drupal/Component/Serialization/[Link]


<?php

namespace Drupal\Component\Serialization;

/**
* Default serialization for JSON.
*
* @ingroup third_party
*/
class Json implements SerializationInterface {

/**
* {@inheritdoc}
*
* Uses HTML-safe strings, with several characters escaped.
*/
public static function encode($variable) {
// Encode <, >, ', &, and ".
return json_encode($variable, JSON_HEX_TAG | JSON_HEX_APOS |
JSON_HEX_AMP | JSON_HEX_QUOT);
}

/**
* {@inheritdoc}
*/
public static function decode($string) {
return json_decode($string, TRUE);
}

/**
* {@inheritdoc}
*/
public static function getFileExtension() {
return 'json';
}
}

La clase Json permite codificar una cadena a Json (método encode()), y viceversa
(método decode()).

Clase Test: /core/tests/Drupal/Tests/Component/Serialization/[Link]


<?php

namespace Drupal\Tests\Component\Serialization;

use Drupal\Component\Serialization\Json;
use Drupal\Tests\UnitTestCase;

/**
* @coversDefaultClass \Drupal\Component\Serialization\Json
* @group Serialization
*/
class JsonTest extends UnitTestCase {

/**
* A test string with the full ASCII table.
*
* @var string

374 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

*/
protected $string;

/**
* An array of unsafe html characters which has to be encoded.
*
* @var array
*/
protected $htmlUnsafe;

/**
* An array of unsafe html characters which are already escaped.
*
* @var array
*/
protected $htmlUnsafeEscaped;

/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();

// Setup a string with the full ASCII table.


// @todo: Add tests for non-ASCII characters and Unicode.
$this->string = '';
for ($i = 1; $i < 128; $i++) {
$this->string .= chr($i);
}

// Characters that must be escaped.


// We check for unescaped " separately.
$this->htmlUnsafe = array('<', '>', '\'', '&');
// The following are the encoded forms of: < > ' & "
$this->htmlUnsafeEscaped = array('\u003C', '\u003E', '\u0027',
'\u0026', '\u0022');
}

/**
* Tests encoding length.
*/
public function testEncodingLength() {
// Verify that JSON encoding produces a string with all of the
characters.
$json = Json::encode($this->string);
$this->assertTrue(strlen($json) > strlen($this->string), 'A
JSON encoded string is larger than the source string.');
}

//...
}

En la clase de test JsonTest, se han programado una serie de pruebas para verificar
el correcto funcionamiento de la clase Json. Vamos a ver, paso a paso, los
elementos que se han definido en esta clase.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 375
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Annotations

En la cabecera de la clase de test se han añadido las siguientes directivas de


Annotations:

- @coversDefaultClass. Referencia a la clase sobre la que se realiza el


test.
- @group. Permite agrupar varios tests en función de la temática.

/**
* @coversDefaultClass \Drupal\Component\Serialization\Json
* @group Serialization
*/
class JsonTest extends UnitTestCase {

Otra directiva muy utilizada es:

- @covers. Esta directiva se incluye en un método implementado dentro de


la clase de Test y hace referencia a un método de la clase evaluada.

En el siguiente ejemplo, la función de test es específica para el método


render() de la clase evaluada.

/**
* Tests the render() method.
*
* @covers ::render
*/
public function testSerializerReceivesOptions() {

Si la prueba es general, no se incluirá la directiva @covers.

Método setUp()

El método setUp() es común y previo a la ejecución de los métodos de pruebas


individuales. Se utiliza, por ejemplo, para definir variables (propiedades de la
clase), que posteriormente se usarán en los métodos de pruebas.

Dentro del método setup() se debe llamar siempre al método de la clase padre:
parent::setUp();

En la clase JsonTest se han declarado las variables $string, $htmlUnsafe y


$htmlUnsafeEscaped. Dentro del método setUp() se le asignan a estas variables
valores de prueba. Estos valores tienen que ser representativos de la prueba que
se vaya a realizar con ellos.

376 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Métodos test* ()

Cada caso o prueba a realizar, se programa en un método independiente, sin


argumentos, cuyo nombre empieza por test. En nuestro ejemplo se implementa la
función testEncodingLength():

/**
* Tests encoding length.
*/
public function testEncodingLength() {
// Verify that JSON encoding produces a string with all of the characters.
$json = Json::encode($this->string);
$this->assertTrue(strlen($json) > strlen($this->string), 'A JSON
encoded string is larger than the source string.');
}

La comprobación implementada se basa en la premisa de que una cadena JSON es


siempre mayor que la cadena original. Para esta prueba se utiliza la cadena
almacenada en $this->string (definida en setUp()). Primero se codifica con el
método de la clase original encode(), y luego se comparan las longitudes.

Métodos assert*()

Las funciones assert*() son las que determinan si la clase evaluada supera la
prueba o no.

Sabemos que, para el funcionamiento de un método específico de la clase que


estamos testeando, se deben cumplir una serie de condiciones, que establecemos
a través de funciones assert*(). Si alguna de las condiciones no se cumple, se
devuelve un error y, opcionalmente, un mensaje de error personalizado (parámetro
$message).

En el método de ejemplo, se utiliza assertTrue(), que comprueba si la condición


es verdadera. En caso contrario, el test devuelve el mensaje de error, y marca la
prueba como no superada.

El listado completo de métodos assert*, así como la descripción y parámetros de


cada uno de ellos, está disponible en:

[Link]

Algunos ejemplos son:

- assertTrue($condition, $message). Comprueba si la condición es TRUE.

- assertFalse($condition, $message). Comprueba si la condición es


FALSE.

- assertEquals($expected, $actual, $message). Comprueba si las


dos variables son iguales.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 377
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

- assertSame($expected, $actual, $message). Comprueba si las


dos variables son exactamente iguales (en tipo y valor).

- assertGreaterThan(), assertGreaterThanOrEqual(), assertLessThan()


y assertLessThanOrEqual(). Comprobaciones de comparación (>, >=,
<, <=, respectivamente).

- assertDirectoryExists(), assertDirectoryIsReadable(),
assertDirectoryIsWritable(). Comprobaciones sobre directorios.

- assertFileEquals(), assertFileExists(), assertFileIsReadable() y


assertFileIsWritable(). Comprobaciones sobre archivos.

- assertContains(), assertNotContains(). Comprueba si una cadena se


encuentra dentro de otra, o si un elemento se encuentra en un array.

- assertRegExp(). Comprueba si una cadena cumple una expresión


regular.

En este otro método de la clase JsonTest, se realiza una prueba de decodificación.


Se comprueba que la cadena codificada y posteriormente decodificada, vuelve a
ser igual que la cadena original. En este caso se utiliza la comprobación
assertSame(), que comprueba que las variables sean iguales tanto en valor como
en tipo.

/**
* Tests converting PHP variables to JSON strings and back.
*/
public function testReversibility() {
$json = Json::encode($this->string);
// Verify that encoding/decoding is reversible.
$json_decoded = Json::decode($json);
$this->assertSame($this->string, $json_decoded, 'Encoding a
string to JSON and decoding back results in the original
string.');
}

378 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Ejemplo: BanMiddlewareTest

El siguiente ejemplo nos servirá para enteder qué son los objetos "mock".

La clase de test BanMiddlewareTest evalúa a la clase \Drupal\ban\BanMiddleware.

Clase a testear: /core/modules/ban/src/[Link]


<?php

namespace Drupal\ban;

use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
* Provides a HTTP middleware to implement IP based banning.
*/
class BanMiddleware implements HttpKernelInterface {

/**
* The decorated kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;

/**
* The ban IP manager.
*
* @var \Drupal\ban\BanIpManagerInterface
*/
protected $banIpManager;

/**
* Constructs a BanMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The decorated kernel.
* @param \Drupal\ban\BanIpManagerInterface $manager
* The ban IP manager.
*/
public function __construct(HttpKernelInterface $http_kernel,
BanIpManagerInterface $manager) {
$this->httpKernel = $http_kernel;
$this->banIpManager = $manager;
}

/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST,
$catch = TRUE) {
$ip = $request->getClientIp();
if ($this->banIpManager->isBanned($ip)) {
return new Response(SafeMarkup::format('@ip has been
banned', ['@ip' => $ip]), 403);
}
return $this->httpKernel->handle($request, $type, $catch);
}
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 379
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Clase Test: /core/modules/ban/tests/src/Unit/[Link]


<?php

namespace Drupal\Tests\ban\Unit;

use Drupal\ban\BanMiddleware;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
* @coversDefaultClass \Drupal\ban\BanMiddleware
* @group ban
*/
class BanMiddlewareTest extends UnitTestCase {

/**
* The mocked wrapped kernel.
*
* @var
\Symfony\Component\HttpKernel\HttpKernelInterface|\PHPUnit_Framework_Mo
ckObject_MockObject
*/
protected $kernel;

/**
* The mocked ban IP manager.
*
* @var
\Drupal\ban\BanIpManagerInterface|\PHPUnit_Framework_MockObject_MockObj
ect
*/
protected $banManager;

/**
* The tested ban middleware.
*
* @var \Drupal\ban\BanMiddleware
*/
protected $banMiddleware;

/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();

$this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$this->banManager = $this->getMock('Drupal\ban\BanIpManagerInterface');
$this->banMiddleware = new BanMiddleware($this->kernel, $this->banManager);
}

/**
* Tests a banned IP.
*/
public function testBannedIp() {
$banned_ip = '[Link]';
$this->banManager->expects($this->once())
->method('isBanned')
->with($banned_ip)
->willReturn(TRUE);

$this->kernel->expects($this->never())
->method('handle');

$request = Request::create('/test-path');
$request->server->set('REMOTE_ADDR', $banned_ip);
$response = $this->banMiddleware->handle($request);

$this->assertEquals(403, $response->getStatusCode());
}

//...
}

380 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Método getMock()

Para instanciar a la clase BanMiddleWare, es necesario pasarle los parámetros:

- $http_kernel, de tipo \Symfony\Component\HttpKernel\HttpKernelInterface


- y $manager, de tipo \Drupal\ban\BanIpManagerInterface

Pero, ¿cómo hacemos para disponer de estos objetos desde la clase de Test? Aquí
entran en juego los objetos "mock", que son objetos de prueba que simulan la
instanciación de una clase determinada del sistema. La idea es que podamos pasar
estos objetos como parámetros a las clases que lo requieran.

protected function setUp() {


parent::setUp();

$this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$this->banManager = $this->getMock('Drupal\ban\BanIpManagerInterface');
$this->banMiddleware = new BanMiddleware($this->kernel, $this->banManager);
}

El método getMock() nos devuelve un objeto mock de la clase solicitada.


Generalmente los asignamos a propiedades de la clase de test, desde setUp(), para
usarlos posteriormente.

En la función setUp() del ejemplo, creamos los objetos mock en $this->kernel y


$this->banManager, y luego los usamos en la instanciación de la clase
BanMiddleware.

Una vez que tenemos un objeto mock, tenemos que prepararlo para que, en
determinadas condiciones, devuelva los valores esperados en el test. En el
siguiente ejemplo, creamos un objeto mock en $this->bar.

Luego indicamos que para el método 'doSomething' (method()), devuelva el valor


'foo' (willReturn()). Esto implica que, al hacer una llamada a
$this->bar->doSomething(), el valor devuelto será 'foo'.

$this->bar = $this->getMock(SomeClass::class);

$this->bar->method('doSomething')
->willReturn('foo');

$this->assertEquals('foo', $this->bar->doSomething());

Esta es la forma de testear clases que dependen de objetos de otras clases,


evitando dependencias que puedan romper la ejecución del test o dar resultados
inesperados.

Volviendo al test de ejemplo, analizamos el método testBannedIp():

public function testBannedIp() {


$banned_ip = '[Link]';
$this->banManager->expects($this->once())
->method('isBanned')
->with($banned_ip)
->willReturn(TRUE);

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 381
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Se asigna una IP cualquiera a la variable $banned_ip, y se le indica al objeto mock


$this->banManager, que cuando alguien le pregunte, a través del método
isBanned, por esa IP, devuelva TRUE. De esta forma estamos indicando por debajo
que esa IP en particular está baneada.

Con expects($this->once()) establecemos la expectativa de que el método


(method()) sea llamado solo una vez y con el parámetro establecido en with().

Puedes profundizar más sobre este tema en:

Test Doubles:
[Link]

Información adicional

Para analizar otros ejemplos de Tests unitarios con PHPUnit, localiza en el núcleo
todos los archivos que contienen " extends UnitTestCase".

PHPUnit (documentación oficial):


[Link]

382 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Tests funcionales con SimpleTest 38.4

Realizan comprobaciones de funcionalidad a un nivel más alto, simulando la carga


del sitio en el navegador y verificando, generalmente, que la salida es la esperada.
Los tests funcionales que dependen de la carga de Drupal (base de datos y
configuración) o de la salida al navegador, los programaremos en Simpletest, que
es un framework específico de Drupal.

Dentro de Simpletest disponemos de dos clases base que extenderemos para crear
la clase de Test:

- \Drupal\simpletest\WebTestBase. Contiene un navegador web


interno y aporta un conjunto de métodos "assert" para realizar
comprobaciones en el entorno. La ejecución del test se hace sobre el perfil
de instalación "testing" incluido en el núcleo. Desde la clase de test se
podrán instalar módulos y realizar otras configuraciones que se puedan
requerir antes de realizar la prueba.

- \Drupal\KernelTests\KernelTestBase. Si no necesitamos verificar la


salida en el navegador, usaremos esta clase base, que es más ligera que
la anterior y no requiere una instalación completa de Drupal. Requiere de
acciones manuales, como creación de tablas e instalación manual de
módulos. No estudiaremos este tipo de test en este curso.

En esta unidad nos centraremos en el primer caso. Los métodos de la clase base
WebTestBase se pueden consultar en:

[Link]
lass/WebTestBase/8

Aunque se trata de un framework propio de Drupal, veremos muchas similitudes


con los métodos estudiados para los tests unitarios con PHPUnit.

Los tests funcionales con Simpletest se ubican en el espacio de nombres


Drupal\mymodule\Tests, dentro de la carpeta del módulo:

mymodule/src/Tests/

El nombre de la clase describirá la prueba a realizar y usará el sufijo Test. Por


ejemplo:

- /mymodule/src/Tests/[Link]

La clase de test incluye un bloque de Annotations con diferentes directivas que


aportan información sobre el test. Generalmente usaremos la directiva @group
para agrupar tests de un mismo grupo bajo una misma etiqueta.

Dentro del mismo Test, cada caso o prueba a realizar, se programará en un método
independiente, sin argumentos, cuyo nombre empieza por test. Por ejempo:
testUsernameUnique().

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 383
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Ejemplo: ForumIndexTest

El siguiente ejemplo nos servirá para analizar la estructura y métodos habituales


utilizados en un Test con simpletest.

La clase ForumIndexTest prueba que funcionan correctamente las siguientes


acciones sobre nodos de tipo forum:

- Crear una entrada en el foro (nodo de tipo forum)


- Crear un foro hijo
- Comprobar que la entrada del foro se muestra en el índice del foro
- Despublicar el contenido y comprobar que ya no se muestra en el índice.

/core/modules/forum/src/Tests/[Link]
<?php

namespace Drupal\forum\Tests;

use Drupal\simpletest\WebTestBase;

/**
* Tests the forum index listing.
*
* @group forum
*/
class ForumIndexTest extends WebTestBase {

/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('taxonomy', 'comment', 'forum');

protected function setUp() {


parent::setUp();

// Create a test user.


$web_user = $this->drupalCreateUser(['create forum content',
'edit own forum content', 'edit any forum content', 'administer
nodes', 'administer forums']);
$this->drupalLogin($web_user);
}
//...
}

En la clase se ha definido la propiedad $modules, que es un array con los


módulos que se deben instalar para realizar el test.

Comenzamos analizando la función setUp(). No debemos olvidar llamar al método


setUp() de la clase padre (parent::setUp()).

Dentro de la función setUp() se crea un usuario de prueba con unos permisos


específicos (drupalCreateUser()), y se accede al sistema con el usuario
(drupalLogin()). De esta forma, todo el test se realizará simulando el punto de
vista de un usuario con los permisos indicados.

384 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

Dentro de la clase se crea el método testForumIndexStatus(), que analizamos paso


a paso. Recuerda que todos estos métodos que se usan internamente provienen
de la clase base WebTestBase y se pueden consultar en la API.

Estamos haciendo un test funcional como si la página se estuviera cargando en el


navegador y el usuario hace clic en los enlaces, rellena campos, etc. Estos pasos
que realizamos sobre la página se van añadiendo al test de forma ordenada, tal y
como lo haríamos al visitar la página.

Nota: Para entender mejor los pasos que se siguen en el test, se recomienda activar
el módulo Forum e ir siguiendo los mismos pasos desde el navegador.

/**
* Tests the forum index for published and unpublished nodes.
*/
function testForumIndexStatus() {
// The forum ID to use.
$tid = 1;

// Create a test node.


$title = $this->randomMachineName(20);
$edit = array(
'title[0][value]' => $title,
'body[0][value]' => $this->randomMachineName(200),
);

En este primer fragmento se utiliza el método $this->randonMachineName(), que


genera un nombre de sistema aleatorio, con el número de caracteres especificado.
Los métodos para generar elementos aleatorios son:

- randomMachineName(). Genera un nombre de sistema único, con


letras y números.

- randomObject(). Genera un objeto PHP aleatorio.

- randomString(). Genera una cadena aleatoria, en formato ASCII.

- randomStringValidate(). Complementa a la función anterior, a modo


de función de callback, para añadir restricciones o reglas que debe cumplir
la cadena a generar.

Nota: si no queremos generar cadenas con caracteres especiales, usaremos el


método randomMachineName(), aunque la cadena no vaya a ser utilizada como un
nombre de sistema.

// Create the forum topic, preselecting the forum ID via a URL parameter.
$this->drupalGet("forum/$tid");
$this->clickLink(t('Add new @node_type', array('@node_type' =>
'Forum topic')));
$this->assertUrl('node/add/forum', array('query' =>
array('forum_id' => $tid)));
$this->drupalPostForm(NULL, $edit, t('Save and publish'));

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 385
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

El método drupalGet() simula la carga de una página de Drupal. En nuestro


ejemplo, como hemos establecido que $tid = 1, estamos cargando la URL /forum/1.

A continuación, hacemos clic en el enlace usando clickLink(). El enlace se


identifica a través del texto del enlace.

Se comprueba si se ha podido cargar la URL del enlace, con assertUrl(), que


comprueba si la URL actual del navegador interno se corresponde con la URL
esperada (node/add/forum).

A continuación, completa y envía el formulario con el método drupalPostForm().

// Check that the node exists in the database.


$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue(!empty($node), 'New forum node found in
database.');

El método drupalGetNodeByTitle() obtiene un nodo de la base de datos basado


en su título. Nota: el método es un alias de getNodeByTitle().

Se comprueba, con assertTrue(), que el nodo obtenido no está vacío y, por tanto,
que se ha creado en la base de datos correctamente.

// Create a child forum.


$edit = array(
'name[0][value]' => $this->randomMachineName(20),
'description[0][value]' => $this->randomMachineName(200),
'parent[0]' => $tid,
);
$this->drupalPostForm('admin/structure/forum/add/forum', $edit,
t('Save'));
$tid_child = $tid + 1;

Desde el listado de administración de foros, añade un foro hijo.

// Verify that the node appears on the index.


$this->drupalGet('forum/' . $tid);
$this->assertText($title, 'Published forum topic appears on
index.');
$this->assertCacheTag('node_list');
$this->assertCacheTag('config:[Link]');
$this->assertCacheTag('comment_list');
$this->assertCacheTag('node:' . $node->id());
$this->assertCacheTag('taxonomy_term:' . $tid);
$this->assertCacheTag('taxonomy_term:' . $tid_child);

Vuelve a la página del foro (/forum/1), y comprueba que se liste el nodo, buscando
por su título (assertText()). La función assertText() busca el texto indicado en
toda la página, limpiando previamente el HTML.

La función assertCacheTag() comprueba que determinadas etiquetas estén


presentes en el objeto Response obtenido al cargar la página.

386 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

// Unpublish the node.


$this->drupalPostForm('node/' . $node->id() . '/edit', array(),
t('Save and unpublish'));
$this->drupalGet('node/' . $node->id());
$this->assertText(t('Access denied'), 'Unpublished node is no
longer accessible.');

Se accede al formulario de edición del nodo y se guarda como no publicado. Luego


se intenta acceder nuevamente a la URL del nodo, con drupalGet(), y se comprueba
que la respuesta sea Acceso denegado (con assertText()).

// Verify that the node no longer appears on the index.


$this->drupalGet('forum/' . $tid);
$this->assertNoText($title, 'Unpublished forum topic no longer
appears on index.');
}
}

Por último, se accede a la página del foro y se comprueba que el nodo no aparece
en la página (assertNoText()).

Como ves, el procedimiento es sencillo, pero al mismo tiempo requiere conocer a


fondo los métodos disponibles para utilizar la comprobación adecuada en cada
situación.

Información adicional

Para analizar otros ejemplos de Tests funcionales con Simpletest, localiza en el


núcleo todos los archivos que contienen "extends WebTestBase".

Más información y ejemplos sobre testing con Simpletest:


[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 387
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

38.5 Tests funcionales con PHPUnit

Aunque por razones históricas se ha venido usando Simpletest para la


programación de los tests funcionales, parece que la inclusión de PHPUnit en el
núcleo de Drupal podría sustituirlo. Por ahora, se pueden utilizar ambos tipos de
tests, sin que exista alguna restricción o recomendación oficial. PHPUnit es un
estándar en muchos frameworks, incluyendo Symfony, y es muy probable que en
el futuro se termine por imponer a Simpletest.

PHPUnit también utiliza un navegador interno que simula la navegación como si se


tratara de un usuario accediendo al sitio. De esta forma, el test puede hacer clic
en enlaces, cargar URLs, enviar formularios, etc.

Los tests funcionales (PHPUnit), se ubican en el espacio de nombres


Drupal\Tests\mymodule\Functional, dentro de la carpeta del módulo:

mymodule/tests/src/Functional

Para crear un test funcional con PHPUnit extenderemos a la clase:

\Drupal\Tests\BrowserTestBase

[Link]
ass/BrowserTestBase/8

El nombre de la clase de test describirá la prueba a realizar y usará el sufijo Test.


Por ejemplo:

- /mymodule/tests/src/Funtional/[Link]

La clase de test incluye un bloque de Annotations con diferentes directivas que


aportan información sobre el test. Al igual que en los tipos de tests anteriores,
añadiremos la directiva @group para agrupar los tests similares.

Dentro del mismo Test, cada caso o prueba a realizar, se programará en un método
independiente, sin argumentos, cuyo nombre empieza por test. Por ejempo:
testFooAddBar().

Métodos assert*()

Muchos de los métodos assert*() disponibles en la clase BrowserTestBase han sido


marcados como obsoletos (deprecated), de cara a ser eliminados en Drupal 9. En
lugar de usar directamente funciones como assertText(), usaremos:

$this->assertSession()->métodoAssert();

Los métodos disponibles están en la clase WebAssert:

[Link]

388 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

bAssert/8

Por ejemplo, en lugar de assertText(), usaremos:

$assert = $this->assertSession()->pageTextContains('foo text');

$assert = $this->assertSession();
$assert->pageTextContains('foo text');

Ten en cuenta que otros métodos como assertTrue(), assertFalse(),


assertUrl() o assertEquals() sí siguen siendo válidos.

Ejemplo: CronExampleTest

Este ejemplo lo hemos extraído del módulo Cron Example (cron_example), incluido
en Examples for Developers.

/modules/contrib/examples/cron_example/tests/src/Functional/[Link]
<?php

namespace Drupal\Tests\cron_example\Functional;

use Drupal\Tests\examples\Functional\ExamplesBrowserTestBase;

/**
* Test the functionality for the Cron Example.
*
* @ingroup cron_example
*
* @group cron_example
* @group examples
*/
class CronExampleTest extends ExamplesBrowserTestBase {

/**
* An editable config object for access to '[Link]'.
*
* @var \Drupal\Core\Config\Config
*/
protected $cronConfig;

/**
* Modules to install.
*
* @var array
*/
public static $modules = ['cron_example', 'node'];

/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create user. Search content permission granted for the search block to
// be shown.
$this->drupalLogin($this->drupalCreateUser(['administer site
configuration', 'access content']));

$this->cronConfig = \Drupal::configFactory()-
>getEditable('[Link]');
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 389
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

La propiedad $modules es un array con los módulos que se deben instalar para
ejecutar el Test.

Dentro de setUp() se crea un nuevo usuario con drupalCreateUser(), indicando los


permisos que tendrá asignado.

También se accede al objeto de configuración '[Link]', añadido por el


módulo.

/**
* Create an example node, test block through admin and user interfaces.
*/
public function testCronExampleBasic() {
$assert = $this->assertSession();

// Pretend that cron has never been run (even though


//simpletest seems to
// run it once...).
\Drupal::state()->set('cron_example.next_execution', 0);
$this->drupalGet('examples/cron-example');

// Initial run should cause cron_example_cron() to fire.


$post = [];
$this->drupalPostForm('examples/cron-example', $post, t('Run
cron now'));
$assert->pageTextContains('cron_example executed at');

Como ya hemos comentado, se utiliza el método assertSession() para acceder a


comprobaciones assert adicionales.

Se carga la página examples/cron-example con drupalGet(), y se ejecuta el cron


enviando el formulario. Luego se comprueba que la página contiene el texto que
verifica que se ha ejecutado el cron.

// Forcing should also cause cron_example_cron() to fire.


$post['cron_reset'] = TRUE;
$this->drupalPostForm(NULL, $post, t('Run cron now'));
$assert->pageTextContains('cron_example executed at');

// But if followed immediately and not forced, it should not fire.


$post['cron_reset'] = FALSE;
$this->drupalPostForm(NULL, $post, t('Run cron now'));
$assert->statusCodeEquals(200);
$assert->pageTextNotContains('cron_example executed at');
$assert->pageTextContains('There are currently 0 items in
queue 1 and 0 items in queue 2');

Se comprueba que, al enviar el formulario, el código de request devuelto es 200.


Luego se comprueba que la página no contiene un texto determinado
($assert->pageTextNotContains()) y que sí contiene otro texto
($assert->pageTextContains()).

390 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 38. Tests automáticos

$post = [
'num_items' => 5,
'queue' => 'cron_example_queue_1',
];
$this->drupalPostForm(NULL, $post, t('Add jobs to queue'));
$assert->pageTextContains('There are currently 5 items in
queue 1 and 0 items in queue 2');

$post = [
'num_items' => 100,
'queue' => 'cron_example_queue_2',
];
$this->drupalPostForm(NULL, $post, t('Add jobs to queue'));
$assert->pageTextContains('There are currently 5 items in
queue 1 and 100 items in queue 2');

Aquí vemos más ejemplos de envío de formulario y de comprobación de texto en


la página.

$this->drupalPostForm('examples/cron-example', [], t('Run cron


now'));
$assert->responseMatches('/Queue 1 worker processed item with
sequence 5 /');
$assert->responseMatches('/Queue 2 worker processed item with
sequence 100 /');
}
}

Por último, el método responseMatches() comprueba que la página HTML de


respuesta cumpla con una expresión regular.

Información adicional

PHPUnit Browser test tutorial


[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 391
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

39 Otras funcionalidades
En esta unidad agrupamos una serie de funcionalidades adicionales
Comparativa D8/D7
que no hemos podido englobar en unidades anteriores.
Envío de emails
Se mantiene hook_mail() como medio
En primer lugar, veremos cómo se componen y envían los emails
para componer el mensaje, pero se ha
desde Drupal.
cambiado la forma de enviarlo, que ahora
se hace a través de un servicio.
Veremos cómo programar tareas de cron, que se ejecutarán junto
Cron
con el cron del sistema.
Se mantiene el formato de hook_cron() de
Drupal 7.
Por último, veremos cómo almacer mensajes en el log del sistema,
Logging
y cómo crear un servicio de log personalizado.
Se han eliminado las funciones watchdog()
y hook_watchdog(). Los mensajes se
registran ahora a través de un servicio
logger.

39
Contenidos de la Unidad
39.1 Envío de emails
39.2 Programación de tareas de cron
39.3 Logging API

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 395
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

39.1 Envío de emails

En Drupal, el envío de emails desde programación se realiza en dos pasos:

1. En primer lugar implementamos hook_mail(), donde se definen una o


varias plantillas de emails, con la configuración de cada uno de ellos
(asunto, cuerpo, encabezados, etc.). El formato es exactamente igual al
empleado en Drupal 7.

2. Para enviar un email usamos el plugin Mail Manager. Es en este punto


podemos modificar la configuración del email para adaptarlo al envío
particular. Por ejemplo, es muy típico asignar aquí el destinatario.

hook_mail()

La función hook_mail() se utiliza para preparar mensajes que posteriormente


serán enviados por correo a través de Mail Manager.

En cada módulo habrá una única implementación de hook_mail(), en la que se


declaran todos los emails que gestiona ese módulo. La función hook_mail() se
implementa en el archivo .module.

[Link]

hook_mail ($key, &$message, $params)

Los parámetros requeridos por hook_mail() son:

- $key. Cadena de texto que servirá para identificar al email, ya que una
misma función hook_mail() puede implementar varios emails.

- $message. Array con la siguiente estructura:

o id. Identificador del correo enviado. El id se compone de


{$module}_{$key}. No hace falta asignar este valor desde
hook_mail, ya que el sistema lo compone a partir de los valores
de $module y $key.

o to. Dirección o direcciones de correo a las que se enviará el


mensaje. Las direcciones de correo se deben separar con comas
(,).

o subject. Asunto del correo. No puede contener saltos de línea.

o body. El cuerpo del mensaje se almacena como un array de


líneas de texto.

El array puede contener líneas de texto u objetos de tipo


MarkupInterface:

396 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

[Link]
nder![Link]/interface/MarkupInterface/8

o from. Dirección de correo desde la que se envía el mensaje.

o headers. Array asociativo en el que podemos añadir los


encabezados del correo: From, Sender, MIME-Version, Content-
Type, etc.

- $params. Array de parámetros adicionales que pueden ser utilizados


para componer el mensaje.

Para explicar el funcionamiento del envío de emails en Drupal, crearemos el módulo


Forcontu Email (forcontu_email). Definiremos un formulario que recogerá
información del correo y lo enviará a su destinatario desde la función de submit.
Solo los usuarios registrados podrán enviar este correo.

Comenzamos con la implementación de hook_mail(), donde declaramos el tipo


de email 'contact_message' ($key), que será el correo que envía el usuario que
completa el formulario al destinatario indicado.

Archivo: /forcontu_email/forcontu_email.module
<?php

use Drupal\Component\Utility\SafeMarkup;

/**
* Implements hook_mail().
*/
function forcontu_email_mail($key, &$message, $params) {
$options = array(
'langcode' => $message['langcode'],
);

switch ($key) {
case 'contact_message':
$username = \Drupal::currentUser()->getDisplayName();

$message['from'] = \Drupal::config('[Link]')->get('mail');

$site_name = \Drupal::config('[Link]')->get('name');
$message['subject'] = t('E-mail sent from @site-name',
array('@site-name' => $site_name), $options);

$message['body'][] = t('@name sent you the following


message:', array('@name' => $username), $options);

$message['body'][] = SafeMarkup::checkPlain($params['message']);
break;
}
}

Dentro del array $message definimos el remitente (from), el asunto (subject) y el


mensaje (body). Una parte del mensaje proviene del formulario que envía el
usuario, y que llega a la función hook_mail() desde $params ($params['message']).

Como es un texto introducido por el usuario, lo filtramos previamente para evitar


que se incluya HTML no permitido (SafeMarkup::checkPlain()).

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 397
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Formulario de ejemplo

A continuación, implementamos el formulario ForcontuEmailMessageForm,


definiendo también la ruta correspondiente (forcontu_email.message, con URL
/forcontu/email/message).

Archivo: /forcontu_email/forcontu_email.[Link]
forcontu_email.message:
path: '/forcontu/email/message'
defaults:
_form: '\Drupal\forcontu_email\Form\ForcontuEmailMessageForm'
requirements:
_role: 'authenticated'

En el formulario hemos pasado algunos servicios que usaremos en submitForm() y


validateForm().

Archivo: /forcontu_email/src/Form/[Link]
<?php

namespace Drupal\forcontu_email\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Mail\MailManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Egulias\EmailValidator\EmailValidator;

class ForcontuEmailMessageForm extends FormBase {

protected $mailManager;
protected $languageManager;
protected $emailValidator;

public function __construct(MailManagerInterface $mail_manager,


LanguageManagerInterface $language_manager,
EmailValidator $email_validator) {

$this->mailManager = $mail_manager;
$this->languageManager = $language_manager;
$this->emailValidator = $email_validator;
}

public static function create(ContainerInterface $container) {


return new static(
$container->get('[Link]'),
$container->get('language_manager'),
$container->get('[Link]')
);
}

public function getFormId() {


return 'forcontu_email_message_form';
}

public function buildForm(array $form, FormStateInterface $form_state) {


$form['intro'] = [
'#markup' => t('Use this form to send a message to an e-mail
address'),
];

$form['message_to'] = [
'#type' => 'email',

398 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

'#title' => $this->t('E-mail address'),


'#required' => TRUE,
];

$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
'#required' => TRUE,
];

$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];

return $form;
}

public function validateForm(array &$form, FormStateInterface $form_state) {

$email = $form_state->getValue('message_to');

if(!$this->emailValidator->isValid($email)) {
$form_state->setErrorByName('message_to', $this->t('%email
is not a valid email address.', ['%email' => $email]));
}
}

public function submitForm(array &$form, FormStateInterface


$form_state) {
// ...
}
}

Aunque aún no hemos definido el submit del formulario, ya podemos instalar el


módulo y acceder a la página /forcontu/email/message' [F39.1a].

F39.1a
Email
Envío de email desde un
formulario.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 399
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

MailManager

El servicio '[Link]', gestionado por la clase MailManager, permite


componer y enviar correos electrónicos.

[Link]
/MailManager/8

En concreto, usaremos el método mail() para enviar el correo:

MailManager::mail($module, $key, $to, $langcode, $params = array(), $reply =


NULL, $send = TRUE)

[Link]
ion/MailManager::mail/8

Los parámetros requeridos por la función mail() son:

- $module. Nombre de módulo cuya función hook_mail() será invocada.

- $key. Identificador del email a enviar. Este identificador permite definir,


dentro de hook_mail(), varias estructuras de email distintas.

- $to. Dirección o direcciones de correos a las que se enviará el mensaje.


Las direcciones deberán ir separadas por coma (,), permitiéndose estos
formatos:
o user1@[Link], user2@[Link]
o User1 <user1@[Link]>, User 2 user2@[Link]

- $langocode. Código del idioma que se utilizará para componer el email.


Generalmente se usará uno de estos tres valores:
o $account->getPreferredLangcode(). Cuando el destinatario
($account) es un usuario del sitio, seleccionamos su preferencia
de idioma.
o \Drupal::currentUser()->getPreferredLangcode(). Cuando el
destinatario es el usuario actual, seleccionamos su preferencia
de idioma.
o \Drupal::languageManager()->getDefaultLanguage()->getId().
Cuando se envía a un email neutral o que no pertenece a un
usuario del sitio, seleccionamos el idioma por defecto del sitio.

- $params. Parámetros opcionales para componer el email.

- $reply. Este valor es opcional (Null), o puede incluir la dirección de


respuesta del email. Aunque se suele corresponder con el valor de 'from',
no siempre es así.

- $send. Estableceremos este valor a TRUE para que se envíe el correo. La


función mail() devuelve un array estructurado con todos los detalles del
mensaje. Si el mensaje ha sido enviado ($send == TRUE), el elemento
'result' del array devuelto contendrá el resultado de la operación.

400 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Veamos cómo utilizar mail() dentro de la función submitForm() del formulario


anterior:

Archivo: /forcontu_email/src/Form/[Link] (submitForm)


public function submitForm(array &$form, FormStateInterface $form_state) {
$form_values = $form_state->cleanValues()->getValues();

$module = 'forcontu_email';
$key = 'contact_message';

$to = $form_values['message_to'];

$params = $form_values;
$language_code = $this->languageManager->getDefaultLanguage()->getId();
$send_now = TRUE;

$result = $this->mailManager->mail($module, $key, $to,


$language_code, $params, NULL, $send_now);

if ($result['result'] == TRUE) {
drupal_set_message($this->t('Your message has been sent.'));
} else {
drupal_set_message($this->t('There was a problem sending
your message and it was not sent.'), 'error');
}
}

A continuación, se muestra el mensaje de correo recibido:

Asunto: E-mail sent from Drupal 8

admin sent you the following message:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut porta nunc in
odio feugiat sodales. Donec sem urna, interdum in turpis laoreet, tempus
faucibus metus. Quisque vehicula interdum turpis ut ornare. Fusce dictum
augue nec tortor vestibulum, nec malesuada dolor ullamcorper. In id sapien
sit amet neque convallis sodales. Duis vehicula congue efficitur.

En nuestro ejemplo, pasamos todos los valores del formulario a hook_mail() través
de $params. El email implementado en hook_mail() está esperando el valor
$params['message'] con el mensaje indicado en el formulario, para incorporarlo al
texto del mensaje del correo. Por medio de $params podemos compartir
información entre el método mail() y hook_mail().

Gracias a la comunicación existente entre hook_mail() y drupal_mail(), algunos


valores relacionados con el mensaje, como el asunto ($message['subject']) o el
cuerpo ($message['body']) pueden definirse en cualquiera de las dos funciones,
siempre usando $params como medio de comunicación entre ambas.

Nota: en el código anterior hemos utilizado el método cleanValues(), que elimina


elementos internos y botones de $form_state, dejando únicamente los valores
realmente enviados a través del formulario.

[Link]
on/FormState::cleanValues/8

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 401
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Función hook_mail_alter()

La función hook_mail_alter() permite modificar los mensajes de email antes de


ser enviados. Cualquier módulo puede interactuar con un mensaje antes de ser
enviado, siempre que conozca su identificador ($message['id']). Como ya hemos
comentado, el id del correo sigue el patrón '$module_$key'. En nuestro módulo de
ejemplo, el id del correo es 'forcontu_email_contact_message'.

[Link]

hook_mail_alter (&$message)

El parámetro $message solicitado por la función hook_mail_alter() será un


array estructurado con los mismos campos que los del parámetro $message en
hook_mail(). A su vez se corresponde con el array $message devuelto por
MailManager::mail(). El parámetro $message se pasa a la función por referencia,
para que pueda ser modificado por cualquier módulo.

En el siguiente ejemplo modificamos el correo enviado por el módulo Contact (del


núcleo de Drupal) identificado con la clave $key == 'page_mail', por lo que el
valor de $message['id'] es 'contact_page_mail'. La modificación realizada
sobre el correo incluye únicamente una línea adicional al final del mensaje que
indica el nombre del sitio desde donde se envió el correo.

/**
* Implements hook_mail_alter().
*/
function forcontu_email_mail_alter(&$message) {

if($message['id'] == 'contact_page_mail'){
$site_name = \Drupal::config('[Link]')->get('name');
$signature = t("\n--\nMail altered by email_example module.",
array(), $options);
$message['body'][] = t("Mail sent out from @site-name",
array('@site-name' => $site_name));
}
}

402 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Programación de tareas de cron 39.2

Drupal cuenta con un sistema de cron que permite la ejecución de tareas


periódicas. El administrador fija el periodo de ejecución del cron (cada día a una
hora determinada, a todas horas, todos los lunes, etc.), momento en que se
ejecutarán todas las acciones definidas en el mismo.

La función hook_cron() permite a cualquier módulo añadir tareas que se


ejecutarán periódicamente, junto con la ejecución del cron.

[Link]

Generalmente el período de ejecución del cron ([Link]) no se define desde


Drupal, sino desde el servidor donde está instalado, a través de la herramienta
crontab). La ejecución manual del cron (desde el área de administración, desde
consola, etc.), también provocará la invocación a todas las funciones hook_cron()
implementadas.

Las tareas que se pueden realizar dentro de hook_cron() pueden ser muy
variadas:

- Eliminar datos temporales de la base de datos.


- Eliminar archivos temporales.
- Realizar cálculos y obtener resultados en background, generalmente en
operaciones que consumen muchos recursos de servidor (por ejemplo,
cálculo de estadísticas de acceso).
- Enviar correos automatizados.
- Obtener datos desde aplicaciones externas.

hook_cron()

Como ejemplo de utilización de hook_cron() analizamos el código implementado


por algunos módulos del sistema.

El módulo History implementa hook_cron() para eliminar de la tabla history los


registros anteriores a una fecha límite, determinada por HISTORY_READ_LIMIT.

/**
* Implements hook_cron().
*/
function history_cron() {
db_delete('history')
->condition('timestamp', HISTORY_READ_LIMIT, '<')
->execute();
}

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 403
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

El módulo dblog implementa hook_cron() para limpiar la tabla de logs del


sistema (watchdog). Primero obtiene el límite de registros que se guarda en la
configuración '[Link]' (variable 'row_limit'). Después, localiza, según este
valor, el identificador ('wid') del último registro que se debe mantener en la base
de datos. A continuación, elimina los registros más antiguos (los que tienen un
valor de 'wid' menor).

<?php

/**
* Implements hook_cron().
*
* Controls the size of the log table, paring it to 'dblog_row_limit' messages.
*/
function dblog_cron() {
// Cleanup the watchdog table.
$row_limit = \Drupal::config('[Link]')->get('row_limit');

// For row limit n, get the wid of the nth row in descending wid order.
// Counting the most recent n rows avoids issues with wid number sequences,
// e.g. auto_increment value > 1 or rows deleted directly from the table.
if ($row_limit > 0) {
$min_row = db_select('watchdog', 'w')
->fields('w', array('wid'))
->orderBy('wid', 'DESC')
->range($row_limit - 1, 1)
->execute()->fetchField();

// Delete all table entries older than the nth row, if nth row was found.
if ($min_row) {
db_delete('watchdog')
->condition('wid', $min_row, '<')
->execute();
}
}
}

Nota: Estos módulos utilizan las funciones obsoletas db_select() y db_delete().


Nosotros implementaremos las consultas a la base de datos de la forma
recomendada que ya hemos estudiado.

Ejecución de cron desde consola

Consulta los comandos de Drush y Drupal Console descritos en el apartado 34.4.

404 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Logging API 39.3

Registrar un evento de log

En Drupal 7 usábamos la función watchdog() para registrar un evento en el log de


eventos del sistema. En Drupal 8 utilizaremos el servicio '[Link]', llamándolo
desde:

\Drupal::logger($channel)->log($level, $message, $context)

- $channel. Es una cadena que identifica un canal de log. Generalmente


se corresponde con el nombre del módulo que realiza el registro.

Los métodos disponibles para un canal de log determinado, incluyendo el método


log(), se pueden consultar en:

[Link]
rface/LoggerInterface/8

El método log() acepta los parámetros:

- $level. Es el nivel o severidad del mensaje o error registrado


(RfcLogLevel::NOTICE, RfcLogLevel::ALERT, RfcLogLevel::EMERGENCY,
etc.). El listado de niveles por defecto se puede consultar en:

[Link]
[Link]/class/RfcLogLevel/8

- $message. Mensaje que se registra. El mensaje estará disponible para


su traducción, sin necesidad de utilizar la función de traducción.

- $context. Array con variables de sustitución para el mensaje.

En realidad solo usaremos el método log() cuando el valor de $level sea dinámico
y solo lo podamos conocer en tiempo de ejecución. Cuando vamos a almacenar un
mensaje de un nivel de severidad determinado, usaremos los otros métodos
disponibles en el canal de log:

- emergency($message, $context)
- alert($message, $context)
- critical($message, $context)
- error($message, $context)
- warning($message, $context)
- notice($message, $context)
- info($message, $context)
- debug($message, $context)

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 405
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

El funcionamiento es igual que la llamada a log(), pero sin el parámetro $level, que
se asigna internamente. Veamos algunos ejemplos:
// Ejemplo log()
\Drupal::logger('php')->log($error['severity_level'], '%type:
@message in %function (line %line of %file) @backtrace_string.',
$error);

// Ejemplo notice()
\Drupal::logger('user')->notice('Deleted %ip', ['%ip' => $this-
>banIp]);

// Ejemplo error()
\Drupal::logger('file system')->error('Could not delete temporary
file "%path" during garbage collection', ['%path' => $file-
>getFileUri()]);

Inyección de un canal de log específico

En estos ejemplos hemos llamado al servicio de forma global, pero, siempre que
sea posible, inyectaremos el servicio en la clase. En este caso, además del servicio,
obtenemos directamente el canal ($channel) de log. En el siguiente ejemplo, se
inyecta el canal de log 'image'.

<?php

namespace Drupal\image;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class ImageEffectBase extends PluginBase implements


ImageEffectInterface, ContainerFactoryPluginInterface {

protected $uuid;
protected $weight = '';
protected $logger;

public function __construct(array $configuration, $plugin_id,


$plugin_definition, LoggerInterface $logger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);

$this->setConfiguration($configuration);
$this->logger = $logger;
}

public static function create(ContainerInterface $container,


array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('[Link]')->get('image')
);
}
//...

A la hora de usar el servicio inyectado, ya no tendremos que indicar el canal, sino


que directamente usaremos el método de log (log(), notice(), error(), etc.):

$this->logger->notice('...');

406 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 39. Otras funcionalidades

Implementar un sistema de log personalizado

Los logs que utilizan el logger por defecto, se almacenan en la tabla watchdog, y
pueden consultarse a través de la interfaz o desde consola, con comandos de Drush
y Drupal Console.

Podemos implementar nuevos sistemas de log que almacenen la información a


registrar en otros destinos y formatos. Para este ejemplo implementaremos el
módulo Forcontu Logger (forcontu_logger).

Para implementar el nuevo servicio de log, seguiremos estos pasos:

1. Registrar el servicio en el archivo .[Link]. En nuestro ejemplo

services:
logger.forcontu_logger:
class: Drupal\forcontu_logger\Logger\ForcontuLoggerLog
tags:
- { name: logger }

2. Crear una clase que implementa Psr\Log\LoggerInterface y utiliza el


trait RfcLoggerTrait, en la carpeta /src/Logger.

<?php

namespace Drupal\forcontu_logger\Logger;

use Drupal\Core\Logger\RfcLoggerTrait;
use Psr\Log\LoggerInterface;

class ForcontuLoggerLog implements LoggerInterface {


use RfcLoggerTrait;

/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array()) {
// Custom log
}
}

Para usar el servicio, lo inyectaremos en la clase como hacemos habitualmente.


Una vez inyectado, haremos uso del nuevo logger sin necesidad de indicar un
canal:

$this->logger->notice('Test message');

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 407
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Experto en Drupal 8 Back-End Development

Este documento forma parte del material didáctico del curso Experto en Drupal 8 Back-End
Development. Si deseas adquirir otros materiales complementarios o realizar el curso online y
certificarte como Experto en Drupal 8 Back-End Development, visita [Link] o ponte
en contacto con nosotros a través del correo info@[Link].

Actualizaciones

Este material será actualizado frecuentemente para nuevas versiones. Si has adquirido la versión
digital en [Link], tendrás acceso permanente y gratuito a las nuevas versiones.

Reporte de errores

Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes hacerlo
directamente completando este formulario (sólo para usuarios registrados en [Link]):

[Link]

Licencia de uso exclusivo

Esta copia es de uso exclusivo y particular para el usuario especificado, que podrá almacenarlo,
imprimirlo y consultarlo en cuantos dispositivos requiera.

Se prohíbe expresamente el uso compartido del documento, la transferencia a otras personas y la


publicación en páginas web o aplicaciones que favorezcan el libre acceso al mismo, ya sean abiertos
al público o privados.

Este documento tampoco podrá ser utilizado en acciones formativas, ya sean online o presenciales,
que no cuenten con la autorización y reconocimiento de Forcontu S.L.

Copyright © 2017 Forcontu S.L.

Todos los derechos reservados. El contenido de este documento, tanto en su totalidad como
parcialmente no puede ser reproducido, almacenado o transmitido de cualquier forma o por cualquier
medio ya sea electrónico, mecánico, fotocopiado, grabado o de otra forma, sin la previa autorización
expresa y escrita por parte de Forcontu S.L. Se incluye, en particular, su mera reproducción y/o
puesta a disposición como resúmenes, reseñas o revistas de prensa, fines para los que también será
necesario contar con la correspondiente autorización de Forcontu S.L. Para obtener más información,
póngase en contacto a través de info@[Link].

Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

40 Compartir en comunidad
La comunidad de usuarios ha sido determinante para el crecimiento
Comparativa D8/D7
y mejora continua de Drupal. En esta última unidad del curso
explicamos las diferentes formas de participar activamente en la A medida que Drupal ha ido
comunidad de Drupal: reportar errores o incidencias en los módulos, evolucionando, también lo han hecho las
crear y compartir módulos, crear y utilizar parches, colaborar en la herramientas que le dan soporte. En esta
traducción de Drupal, etc. unidad no podemos hablar de diferencias
entre versiones, pero si de una evolución
continua del software y las herramientas
utilizadas para dar soporte al desarrollo de
Drupal.

40
Contenidos de la Unidad
40.1 La Comunidad de Drupal
40.2 Reportar incidencias
40.3 Compartir un proyecto en [Link]
40.4 Crear y aplicar parches
40.5 Colaborar en la traducción de Drupal

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 411
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

40.1 La Comunidad de Drupal

La comunidad de usuarios ha sido determinante para el crecimiento y mejora


continua de Drupal. Las formas de participar en la comunidad son muy diversas:
ayudar a otros usuarios que se están iniciando en Drupal, organizar eventos,
traducir módulos, reportar errores, etc.

En este enlace encontrarás una guía inicial (en inglés) sobre las diferentes formas
de participar en la comunidad.

[Link]

Comenzamos esta unidad con una breve introducción a los eventos, asociaciones
y puntos de encuentro de la comunidad de Drupal.

Eventos

Existen diferentes tipos de eventos relacionados con Drupal y organizados por la


comunidad, en función del alcance geográfico:

- DrupalCon. Es el evento internacional de Drupal. Es un evento


totalmente en inglés, que reúne a personas de todo el mundo.
Generalmente se organiza dos veces al año, uno en Estados Unidos y otro
en Europa, aunque en alguna ocasión se ha extendido a otros continentes.

- DrupalCamp. Las DrupalCamp son eventos que generalmente tienen un


alcance estatal y que se celebran en el idioma del país anfitrión. Por
ejemplo, en España se celebra la Drupalcamp Spain.

- DrupalCamp Spain. La primera DrupalCamp Spain se celebró en 2010


en Barcelona, donde se gestó el nacimiento de la Asociación Española de
Drupal (AED). Desde entonces la AED ofrece el apoyo necesario para
garantizar la celebración anual de la Drupalcamp (Barcelona 2010, Sevilla
2011, Madrid 2012, Cáceres 2013, Valencia 2014, Jerez 2015, Granada
2016, Madrid 2017).

- Drupal Developer Days. Este evento está orientado a desarrolladores


de Drupal. Es un evento internacional, de menor calado que las
DrupalCon, pero al que asisten desarrolladores de otros países.

- Drupal Day. Este evento se celebra en España y está promovido por la


AED. Se trata de un evento de un único día cuyo objetivo es crear un
espacio de encuentro para profesionales de Drupal.

Estos son solo algunos de los eventos que se celebran en torno a Drupal. Busca tu
comunidad local y pregunta por los eventos que se celebrarán próximamente en
tu zona o, porqué no, anímate a organizar un encuentro informal para conocer a
otros usuarios y profesionales de Drupal.

412 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Asociaciones de Drupal

Destacamos principalmente dos asociaciones:

- Drupal Association ([Link] Es la asociación


internacional de Drupal, cuya misión es hacer crecer Drupal. La Drupal
Association acepta dos tipos de socios: personas individuales y
organizaciones. En ambos casos, además de colaborar con tu aportación
económica, dispondrás de un badge o insignia que podrás utilizar en tu
web, blog, etc.

- Asociación Española de Drupal ([Link] La


Asociación Española de Drupal (AED) pretende ser referencia y punto de
encuentro en España para aficionados y profesionales de Drupal. Uno de
los objetivos de la AED es dar el apoyo organizativo para garantizar la
celebración anual de la DrupalCamp Spain, además de otros eventos
como el Drupal Day.

Puntos de encuentro online

Hay muchos medios para entrar en contacto con otros miembros de la comunidad,
ya sea para buscar información, hacer consultas, resolver dudas, organizar
eventos, etc. Algunos de estos medios son:

- [Link] ([Link] Es el sitio principal de Drupal, en inglés.


Tener un usuario en [Link] es fundamental para participar en muchas
de las herramientas que incluimos en este apartado.

- Foros de Drupal ([Link] Son la base para solicitar


soporte a otros usuarios, publicar servicios profesionales, etc. En este
medio toda la comunicación es en inglés.

- Grupos de Drupal ([Link] Puedes unirte a cualquier


grupo con el que tengas alguna afinidad. Encontrarás grupos
internacionales, en inglés, pero también grupos locales, en español
(Spanish association, Drupalcamp Spain, Madrid, etc.).

- Grupos de Google. Dentro de los grupos de Google destacamos el grupo


Drupal en Español, un punto de encuentro principalmente para realizar
consultas técnicas.

- Páginas y grupos en Facebook. En Facebook encontrarás muchas


páginas y grupos relacionados con Drupal. Destacamos el grupo Drupal
en Español y la página de Drupal (en inglés).

- Twitter. En Twitter también podrás seguir a muchos profesionales de


Drupal. @drupal es el usuario oficial de [Link]. Los hashtags más
utilizados por la comunidad son #drupal (internacional) y #drupal_es (en
español).

- IRC. La lista completa de canales para comunicarse vía IRC está


disponible en: [Link] Algunos de los canales
más importantes son #drupal-support, #drupal y #drupal-es, este último
en español.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 413
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

- Linkedin. En esta red profesional también encontrarás grupos donde se


habla de Drupal. En el grupo Drupal España encontrarás, además, una
importante demanda de desarrolladores de Drupal.

- Planet Drupal ([Link] Es una página donde se


agrega contenido de diferentes blogs especializados en Drupal (en inglés).

- Drupical ([Link] Mapa mundial con los próximos


eventos de Drupal. Caben todos los eventos, desde los más grandes a los
más pequeños.

414 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Reportar incidencias 40.2

Desde la página oficial de cada proyecto (módulo, tema, perfil de instalación, etc.)
podemos contactar con los desarrolladores que lo mantienen a través de la cola
de incidencias (Issues), teniendo en cuenta que toda la comunicación debe
hacerse en inglés.

Existen normas para gestionar la cola de issues, así que siempre es recomendable
leer la información actualizada, ya que puede sufrir cambios:

[Link]

La cola de incidencias de cada proyecto se muestra la columna de la derecha.

F40.2a
Incidencias (Issues)
Cada proyecto tiene su
propia cola de incidencias,
donde podemos encontrar
los errores reportados por
otros usuarios o publicar los
errores encontrados.
Siempre se deben revisar
las incidencias ya
publicadas antes de realizar
una nueva consulta.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 415
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Para evitar duplicar consultas, antes de publicar cualquier incidencia es altamente


recomendable buscar en el listado de tareas si nuestra consulta está ya publicada.
Además, es muy probable que ya se esté discutiendo el problema e incluso que se
haya aportado alguna solución.

Para localizar una incidencia, selección la versión de Drupal (8.x issues) y, en el


campo Search for, escribe parte del error obtenido o una descripción del mismo,
siempre en inglés [F40.2b].

F40.2b
Cola de incidencias
Listado de incidencias de
un proyecto. Podemos
filtrar las incidencias por
categoría, versión del
módulo e indicando la
cadena a buscar.

En la cola de incidencias no sólo se publican errores, también podemos publicar


tareas, solicitudes de nuevas funcionalidades o incluso solicitud de ayuda o soporte
técnico. Siempre debes tener en cuenta que las personas que mantienen un
proyecto lo hacen de forma totalmente altruista, por lo que no debes exigir ni
esperar una respuesta obligatoria a tu consulta.

Crear una incidencia

Para crear una nueva incidencia usaremos el enlace Create a new issue, que solo
estará disponible si nos hemos logueado previamente en el sistema [F40.2c].

F40.2c
Crear una incidencia
Enlace para crear una
nueva incidencia.

416 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Recuerda que la incidencia debe escribirse en inglés, siguiendo una serie de


normas:

[Link]

Algunos de los campos a rellenar son [F40.2d]:

- Información del proyecto:

o Proyecto (Project). Nombre del proyecto. Si estamos creando


la incidencia desde la página del proyecto, no podremos cambiar
este valor.

o Versión (Version). Selecciona la versión del proyecto donde has


encontrado la incidencia.

o Componente (Component). El componente hace referencia a


la categoría dentro del proyecto. Los valores disponibles pueden
variar de un proyecto a otro. Posibles valores son: Código (code),
Documentación (documentation), Miscelánea (Miscellaneous) e
Interfaz de usuario (User interface). También se suelen incluir
los submódulos del proyecto.

- Information de la incidencia:

o Categoría (Category). Las categorías disponibles son:


▪ Bug report (reportar un error).
▪ Task (tarea).
▪ Feature request (solicitar nueva funcionalidad).
▪ Support request (solicitar ayuda).
▪ Plan.

o Prioridad (Priority). Selecciona la prioridad de la incidencia. Ten


en cuenta que la prioridad debe establecerse en base al tipo de
error encontrado, y no tanto a las necesidades particulares que
puedas tener en tu proyecto. Los valores posibles son Normal
(por defecto), Critical, Major, Minor.

o Asignada a (Assigned). Si es un trabajo que vas a realizar tú


mismo, podrás asignarte la tarea desde esta opción.

o Estado (Status). El estado inicial será activa (active). El


moderador del proyecto podrá cambiar este estado.

- Detalles de la incidencia:

o Título (Title). Añade un texto suficientemente descriptivo de la


incidencia.

o Descripción (Description). Explica con detalle la incidencia.


Añade el código que creas conveniente, un enlace con el error,
etc. Utiliza la plantilla disponible para describir correctamente la
incidencia: [Link]

o Etiquetas (Issue tags). Permite clasificar la incidencia.

o Archivos adjuntos (File attachments). Añade los archivos que

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 417
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

consideres necesarios para explicar la incidencia. Por ejemplo,


una captura con el error, un parche, etc.

- Incidencia padre (Parent issue). Si existe una incidencia padre que


englobe a la incidencia actual, la seleccionaremos aquí.

- Incidencias relacionadas (Related issues). Si existen incidencias


relacionadas, las buscaremos y seleccionaremos aquí.

F40.2d
Crear una incidencia
Formulario de creación de
incidencia.

Una vez enviada la incidencia, sé paciente. Recibirás un correo cada vez que algún
usuario publique un comentario en tu incidencia.

Si deseas seguir una incidencia publicada por otro usuario, haz clic en el enlace
Follow que encontrarás a la derecha [F40.2e].

F40.2e
Seguir una incidencia
Enlace Follow para seguir
las actualizaciones de
cualquier incidencia.

Reportar una incidencia de seguridad

No reportes nunca una incidencia de seguridad directamente en la cola de issues.


En la página del módulo encontrás un enlace "Report a security vulnerability", que
te llevará a un formulario específico. Consulta el procedimiento completo en:

[Link]

418 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Compartir un proyecto en [Link] 40.3

La filosofía del trabajo en comunidad es "no compitas, colabora". Por eso, antes de
crear un nuevo proyecto (módulo, tema o perfil de instalación), consulta si ya existe
un proyecto similar. Si es así, tendrá más valor colaborar en el módulo existente
ayudando a mantenerlo y ampliarlo, en lugar de comenzar desde cero nuevas
líneas de trabajo.

Puedes consultar aquí todas las instrucciones para compartir un proyecto en


[Link]:

[Link]

Consulta, en particular, las buenas prácticas para crear y mantener proyectos:

[Link]

Documentación del módulo

Compartir y mantener un proyecto en [Link] va mucho más allá de subir el


código al repositorio de módulos. Para empezar el proyecto debe estar bien
documentado, incluyendo algunos de los siguientes archivos:

- [Link] (obligatorio). Información descriptiva del proyecto,


instrucciones de uso e instalación, etc.

- [Link]. Es un archivo opcional, necesario sobre todo si las


instrucciones de instalación son extensas y conviene separarlas del
archivo [Link].

- Página del proyecto. En la página del proyecto incluiremos una


descripción resumida del mismo. No debemos extendernos demasiado,
dejando la información completa para el archivo [Link] o la página
de documentación del proyecto.

- [Link]. Registra los cambios de versión realizados en el


proyecto.

- Página de documentación del proyecto en [Link]. El proyecto


puede tener una sección en el área de documentación de [Link].
Puede incluir, por ejemplo, la información contenida en los archivos
[Link] y [Link].

Estándares de codificación

El proyecto debe seguir los estándares de codificación, ya estudiados en este


curso. Para ello se recomienda utilizar el módulo Coder y comprobar que el código
es correcto.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 419
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Traducción

Recuerda también que el proyecto debe estar en inglés: nombre de variables y


funciones, comentarios en el código, etc. Si queremos incluir la traducción del
módulo a español, genera la traducción y compártela en [Link].

Git: repositorio de proyectos y control de versiones

El mejor sitio para subir y compartir un proyecto relacionado con Drupal es el


repositorio oficial de [Link]. Si tu módulo no está disponible en este
repositorio, no podrá tener su página en [Link] y, por tanto, no se anunciará
junto al resto de módulos. También debemos tener en cuenta que, para que un
proyecto pueda subirse al repositorio oficial, debe hacerse bajo licencia GNU GPL
versión 2 o superior, que es la misma licencia con que se distribuye Drupal.

Si el módulo tiene componentes (por ejemplo, una librería) que no se pueden


distribuir con esta licencia, se puede compartir el módulo base, detallando en las
instrucciones cómo descargar e instalar los componentes adicionales.

El repositorio de archivos utilizado en Drupal es Git, que es un sistema de control


de versiones libre que permite la publicación de archivos de forma distribuida. Los
desarrolladores pueden trabajar en local sobre una copia completa del repositorio,
compartiendo entre ellos y el repositorio central los cambios publicados.

Para utilizar el repositorio primero debemos obtener acceso a Git. Lo haremos


desde la pestaña Git access, dentro de la edición de nuestra cuenta de usuario en
[Link]. Debemos indicar el nombre de usuario de Git, y aceptar los términos
de uso.

Encontrarás más información sobre la solicitud de acceso a Git en:

[Link]

F40.3a
Acceso a Git
Desde la pestaña Git
access, en la edición de
nuestra cuenta de
[Link], podemos
solicitar el acceso al
repositorio de proyectos de
Drupal.

420 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Instalación y configuración de Git

Para utilizar Git tenemos que instalarlo en nuestro equipo. Descarga la última
versión disponible para tu sistema operativo desde [Link]

Una vez instalado, accediendo a la aplicación Git Bash abriremos la consola o línea
de comandos de Git, que utiliza los comandos habituales de linux (ls, cd, mkdir,
etc.).

Desde la línea de comandos debemos establecer los valores de [Link] y


[Link]. Es importante que el email indicado se corresponda con el email de
nuestra cuenta de Drupal, ya que las cuentas estarán vinculadas a través de este
campo.

git config --global [Link] "User name"


git config --global [Link] user@[Link]

Una vez modificados estos parámetros de configuración, podemos comprobar si


los parámetros han sido añadidos a Git con el comando:

git config -l

La contraseña del usuario de [Link] será también la contraseña de Git.

El proceso de instalación y configuración de Git se detalla en:

[Link]

Crear un proyecto de prueba (Sandbox)

Drupal permite la creación de proyectos en modo Sandbox o desarrollo. Esto nos


permitirá experimentar con el código de módulo o tema antes de compartirlo con
el resto de la comunidad como un proyecto completo (Full Project).

[Link]

Aunque el módulo esté en modo Sandbox, estará accesible a través del repositorio
de módulos, seleccionando en el filtro de estado Todos los proyectos o Sólo los
proyectos en Sandbox.

La diferencia fundamental entre un proyecto completo y un proyecto en Sandbox


es que en el segundo caso no es posible publicar versiones del mismo. Si otro
usuario desea obtener el código del proyecto, sólo podrá descargarlo usando Git.

Antes de tener acceso al repositorio, necesitamos crear un proyecto en desarrollo


(sandbox), desde el enlace Add a new project (module, theme, distribution)
en [F40.3b]:

[Link]

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 421
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

F40.3b
Proyecto en sandbox
Podemos crear proyectos
de prueba o Sandbox, que
estarán disponibles en el
repositorio pero que son
considerados
experimentales.

Para crear el proyecto seleccionaremos su tipo (módulo, tema o perfil de


instalación, entre otros), las categorías con las que está relacionado, el estado de
mantenimiento y desarrollo y, por último, el título y descripción del proyecto
[F40.3c].

F40.3c
Crear proyecto
Para crear un proyecto
seleccionamos su tipo
(módulo, tema, perfil de
instalación, etc.), la
categoría, el estado de
mantenimiento y desarrollo
y el título y descripción del
proyecto (en inglés).

Hemos creado el proyecto Forcontu Test Project, que nos servirá en este curso
para realizar pruebas con el repositorio. El módulo está disponible en [F40.3d]:

[Link]

422 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

F40.3d
Página del proyecto
Página de un proyecto en
sandbox. El proyecto se
añade automáticamente al
repositorio de versiones Git
de Drupal.

En la pestaña Version control de cada proyecto se muestran las instrucciones


para su descarga del repositorio. Como aún no hemos subido el código del
proyecto, las instrucciones que se muestran inicialmente explican los pasos a seguir
para subir el proyecto al repositorio [F40.3e].

F40.3e
Control de versiones
del proyecto
Desde la pestaña Version
control se muestran las
instrucciones para subir el
proyecto al repositorio.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 423
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Se recomienda crear una carpeta raíz para el repositorio, donde almacenaremos


todos los proyectos sincronizados con el repositorio de [Link].

En nuestro caso hemos creado la carpeta local h:/workspace/[Link],


donde iremos creando los proyectos subidos (o descargados) del repositorio de
[Link]. Dentro de esta carpeta hemos añadido la carpeta del proyecto de
prueba, forcontu_test_project, que ya incluye los archivos .[Link], .module
y .install.

Para sincronizar la carpeta del módulo con el repositorio, accederemos a la carpeta


desde la línea de comandos de Git. Utiliza los comandos habituales de linux para
navegar hasta la carpeta. Seguiremos las instrucciones de la página del proyecto,
pero teniendo en cuenta que ya tenemos creada la carpeta del proyecto [F40.3f].

$ cd forcontu_test_project/
F40.3f
Comandos para subir el $ git init
proyecto al repositorio Initialized empty Git repository in
h:/workspace/[Link]/forcontu_test_project/.git/
Comandos que
ejecutaremos desde la $ git add forcontu_test_project.[Link]
consola de Git para crear
el proyecto en local y $ git add forcontu_test_project.module
subirlo al repositorio de
[Link], de forma que $ git add forcontu_test_project.install
queden sincronizados.
$ git commit -m "Initial commit."
[master (root-commit) e9b7b18] Initial commit.
3 files changed, 379 insertions(+), 0 deletions(-)
create mode 100644 forcontu_test_project.[Link]
create mode 100644 forcontu_test_project.install
create mode 100644 forcontu_test_project.module

$ git remote add origin forcontu@[Link]:sandbox/forcontu/[Link]

$ git push origin master


The authenticity of host '[Link] ([Link])' can't be
established.
RSA key fingerprint is [Link].
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[Link],[Link]' (RSA) to the
list of known hosts.
forcontu@[Link]'s password:
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 2.70 KiB, done.
Total 5 (delta 0), reused 0 (delta 0)
To forcontu@[Link]:sandbox/forcontu/[Link]
* [new branch] master -> master

Dentro de la carpeta, el comando "git init" inicializa el repositorio para este


proyecto en particular, creando la carpeta .git (carpeta oculta). Aunque la carpeta
contenga archivos, el repositorio se considera inicialmente vacío, teniendo que
añadir cada archivo haciendo uso del comando "git add".

Una vez añadidos los archivos, con "git commit" creamos una primera versión
(commit) del proyecto.

Con "git remote add origin" establecemos la relación entre el repositorio local y
el repositorio en [Link].

424 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Una vez establecida esta relación, con "git push origin master" estaremos
subiendo los cambios locales (establecidos en el commit inicial) al repositorio en
[Link]. En este punto se requiere autenticación de acceso al repositorio,
que puede hacerse por dos métodos:

- Definiendo claves SSH. Este mecanismo permite que la identificación


sea directa, a través de las claves definidas en el equipo local. El proceso
de creación de claves SSH se detalla en: [Link]

- Contraseña de [Link]. Facilitando la contraseña de acceso al


repositorio, que se corresponde con la de nuestro usuario en [Link].

En nuestro ejemplo hemos utilizado el segundo método. Una vez correctamente


autenticados, los archivos serán subidos al servidor de versiones, por lo que
cualquier usuario podrá descargarlos.

Si volvemos a la pestaña Version control de nuestro proyecto, veremos que las


instrucciones han cambiado. Estas instrucciones, que son comunes a todos los
proyectos, permiten que cualquier usuario de la comunidad pueda colaborar en el
proyecto, subir cambios, parches, etc [F40.3g].

F40.3g
Instrucciones para
otros usuarios
Una vez hayamos subido el
proyecto al repositorio, las
instrucciones mostradas en
la pestaña Version control
servirán para que cualquier
usuario pueda descargar el
proyecto, crear parches,
etc.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 425
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Actualizar los cambios en el proyecto

Si modificamos cualquiera de los archivos en la versión local del proyecto,


tendremos que subir los cambios al repositorio. La acción de publicar cambios en
el proyecto se denomina, en inglés, commit.

Comenzamos con el comando "git add -A", que busca en la carpeta (y subcarpetas)
todos los cambios que se hayan realizado en el proyecto.

A continuación, definimos el commit con git commit - m "Descripción de la


versión". Cada vez que subimos un cambio, conviene identificar el cambio
realizado. Si el cambio es realizado por otro usuario, los mantenedores del módulo
pueden decidir si aplicar o no el cambio en la siguiente versión del módulo. En esta
URL encontrarás más información sobre el formato que deben tener los mensajes
en cada commit: [Link] La versión más corta es:

Issue #[número de incidencia] by [usuarios que han participado en la solución]:


[Resumen corto del cambio realizado].

Por último, para subir el commit al servidor de versiones, ejecutaremos el comando


"git push -u origin master".

$ git add -A

$ git commit -m "Issue #12 by forcontu: Added module dependencies."


[master a12ae35] Issue #12 by forcontu: Added module dependencies.
1 files changed, 1 insertions(+), 0 deletions(-)

$ git push -u origin master


forcontu@[Link]'s password:
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 347 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To forcontu@[Link]:sandbox/forcontu/[Link]
e9b7b18..a12ae35 master -> master
Branch master set up to track remote branch master from origin.

Los commits subidos al repositorio se pueden consultar desde la página del


proyecto, en la columna de la derecha (View commits) [F40.3h].

F40.3h
Commits
Cada cambio subido al
repositorio se denomina
commit. Desde la página
del proyecto podemos
consultar el listado de
commits del proyecto.

En nuestro ejemplo veremos el commit inicial y el segundo commit realizado,


ambos con la descripción correspondiente [F40.3i].

426 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

F40.3i
Listado de commits
En el listado de commits se
muestran todos los
cambios realizados en el
proyecto. Es por ello que
al subir un cambio
conviene incluir una
descripción corta del
cambio publicado.

Gestión de incidencias

Una vez publicado el proyecto, otros usuarios podrán enviar incidencias a través
de la cola de incidencias (issues). Desde cada incidencia podemos mantener una
conversación activa con otros usuarios, siempre en inglés, para averiguar el origen
de la incidencia o las posibles soluciones [F40.3j].

F40.3j
Gestión de incidencias
Nuestro proyecto también
tiene su propia cola de
incidencias, donde otros
usuarios podrán publicar
errores, solicitar ayuda,
etc.

Una vez solucionada la incidencia, cambiaremos su estado a fixed (corregido), o


a cualquiera de los estados disponibles. Si la corrección de la incidencia lleva
asociado un commit, conviene indicar en el texto descriptivo del commit el número
de la incidencia y los usuarios que han participado en su identificación y solución
[F40.3k].

F40.3k
Cerrar incidencias
Conviene hacer una
correcta gestión de las
incidencias, cerrando
aquellas que ya están
solucionadas.

Las incidencias que no han tenido actividad durante 2 semanas se cerrarán


automáticamente, pasando al estado closed (fixed). En cualquier momento
podremos reabrir la incidencia y seguir trabajando en ella.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 427
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

40.4 Crear y aplicar parches

Descargar otros proyectos

Si queremos colaborar con una modificación en el código de otro proyecto, primero


tendremos que descargarlo en nuestro repositorio local de Git. Las instrucciones
para descargar otro proyecto las encontramos en la página de cada proyecto,
pestaña Version control [F40.4a].

F40.4a
Descargar proyecto
En la pestaña Version
control se muestran las
instrucciones para
descargar otros proyectos
desde el repositorio.

Una vez ejecutado el comando "git clone", tendremos disponible en local la última
versión del módulo obtenida del repositorio remoto. Para descargar los archivos no
hemos necesitado autenticarnos, sólo tendremos que hacerlo al subir archivos
[F40.4b].

F40.4b $ git clone --branch master [Link]


forcontu_test_project
Comandos para Cloning into 'forcontu_test_project'...
descargar proyecto remote: Counting objects: 8, done.
Comandos Git para remote: Compressing objects: 100% (8/8), done.
descargar el proyecto. Una remote: Total 8 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.
vez descargado tendremos
acceso a todos los archivos $ cd forcontu_test_project/
desde el equipo local.
$ ls
forcontu_test_project.info forcontu_test_project.install
forcontu_test_project.module

Qué es un parche (patch)

Un parche es el resultado de evaluar las diferencias entre dos versiones de un


mismo archivo (o un conjunto de archivos). Este resultado se genera con el
comando "git diff" y se almacena en forma de archivo de texto, con la
extensión .patch, que tendrá una estructura de este tipo [F40.4c]:

F40.4c diff --git a/token_example/token_example.[Link]


b/token_example/token_example.[Link]
Parche (patch) index 585dcea..b06d9d6 100644
Ejemplo de archivo .patch. --- a/token_example/token_example.[Link]
Un parche muestra las +++ b/token_example/token_example.[Link]
diferencias entre el archivo @@ -13,8 +13,8 @@ function token_example_token_info() {
// second is the user's default text format, which is itself a 'format' token
original y el nuevo archivo. // type so it can be used directly.

- // Comentario en el archivo original que será eliminado


+ // Nuevas líneas de código que no estaban en el archivo original
+ // y se añadirán al archivo al aplicar el parche
$info['types']['format'] = array(
'name' => t('Text formats'),
'description' => t('Tokens related to text formats.'),

428 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Las primeras líneas identifican el archivo que está siendo modificado, que en el
ejemplo es token_example/tokem_example.[Link].

diff --git a/token_example/token_example.[Link]


b/token_example/token_example.[Link]
index 585dcea..b06d9d6 100644
--- a/token_example/token_example.[Link]
+++ b/token_example/token_example.[Link]

Las líneas que comienzan por --- indican que se sustituirán por la línea con +++.

La siguiente línea indica que el cambio está dentro de la función


token_example_token_inf(), sobre la línea 13.

@@ -13,8 +13,8 @@ function token_example_token_info() {

Las líneas que comienzan con - serán eliminadas.

- // Comentario en el archivo original que será eliminado

Las líneas que comienzan por + serán añadidas al archivo en el punto en que se
indica.

+ // Nuevas líneas de código que no estaban en el archivo original


+ // y se añadirán al archivo al aplicar el parche

Las líneas que no incluyen ningún símbolo se mantendrán igual que en el archivo
original.

En el archivo final no se mostrarán estas etiquetas anteriores (+++, ---, +, -, etc.),


que se utilizan únicamente para señalar los cambios en el parche.

Crear un parche con Git

Los parches en Drupal son necesarios cuando se descubre algún error en un


módulo y se requiere su modificación. Podemos encontrarnos en dos situaciones,
en función de si actuamos como proveedor o consumidor del parche:

- Hemos descubierto un error en un módulo y queremos aportar la solución


para que sea incorporada en la siguiente versión del módulo. En este caso
crearemos un parche y lo compartiremos a través de la cola de
incidencias del módulo.

- Un usuario ha aportado un parche, a través de la cola de incidencias, que


aún no ha sido incluido en la siguiente versión del módulo. Si queremos
corregir el error, tendremos que aplicar el parche en el módulo de
nuestro sitio web.

Los comandos para crear un parche se detallan en la pestaña Version control del
módulo que estamos modificando [F40.4d].

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 429
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

F40.4d
Crear un parche
Las instrucciones para
crear un parche con Git se
incluyen en la pestaña
Version control del
módulo, aunque son
comunes para todos los
módulos.

Es importante utilizar un nombre adecuado para el parche. Una buena práctica es


utilizar la siguiente nomenclatura:

[módulo]-[descripción]-[número-incidencia]-[número-comentario].patch

Por ejemplo, un parche para el módulo Pathauto relacionado con los títulos y que
va a ser publicado en el comentario #95 de la incidencia #12345 debería tener el
nombre:

[Link]

En esta página encontrarás más información sobre las buenas prácticas a la hora
de publicar un parche:

[Link]

Para crear el parche primero modificamos el archivo o archivos del proyecto que
previamente hemos descargado desde el repositorio. El comando "git diff" se
encargará de generar el parche con las diferencias [F40.4e]:

F40.4e $ git diff > forcontu_test_project-[Link]


Crear un parche con Git
$ ls
Comandos para crear un forcontu_test_project-[Link]
parche usando Git. forcontu_test_project.install
forcontu_test_project.info
forcontu_test_project.module

El parche se generará en la carpeta del módulo. En nuestro ejemplo hemos


modificado el archivo forcontu_test-[Link] para eliminar la dependencia
con el módulo views y añadir dependencias con panels y pathauto [F40.4f].

430 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

diff --git a/forcontu_test_project.info F40.4f


b/forcontu_test_project.info
Parche creado
index f38e595..aab5a2c 100644
--- a/forcontu_test_project.info Ejemplo de parche creado
+++ b/forcontu_test_project.info con Git.
@@ -2,5 +2,6 @@ name = "Forcontu Test Project"
description = "Test project for educational purposes"
core = 7.x
files[] = forcontu_test_project.module
-dependencies[] = views
+dependencies[] = panels
+dependencies[] = pathauto
package = My Modules
\ No newline at end of file

Adjuntar un parche a una incidencia

La forma de compartir un parche con otros usuarios o con el mantenedor del


proyecto para que lo tenga en cuenta en las siguientes versiones del módulo es a
través de la cola de incidencias.

Cuando adjuntamos un parche a una incidencia debemos cambiar el estado a


"Needs review" o "Needs work". De esta forma el parche será revisado por el
sistema de comprobación automática de parches. Este sistema, que no se aplica
en los proyectos en Sandbox, comprueba si el parche se ha generado
correctamente, ahorrando trabajo a los mantenedores del proyecto [F40.4g].

F40.4g
Publicar un parche
Los parches se publican
como archivos adjuntos a
una incidencia.

Aplicar un parche

Aplicar un parche con git es muy sencillo. Primero descargaremos la versión del
módulo para la que se ha creado el parche. El módulo puede descargarse o bien
desde la página del módulo, o bien desde el repositorio, usando Git.

A continuación descargaremos el parche (archivo .patch) en la carpeta del módulo.


Los parches se encuentran, generalmente, en los comentarios de las incidencias
de un módulo.

Desde la consola de Git escribiremos el comando:

$ git apply -v [[Link]]

$ rm [[Link]]

Se recomienda eliminar el parche una vez aplicado (comando "rm").

Una vez aplicado el parche, subiremos el módulo modificado a nuestro sitio web.
En general, los parches no deben aplicarse directamente en un servidor en
producción o, al menos, deben tomarse las precauciones adecuadas, como realizar
una copia de seguridad del sitio.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 431
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

40.5 Colaborar en la traducción de Drupal

Otra forma de colaborar con la Comunidad es aportando y revisando las


traducciones de Drupal, tanto del núcleo como de los módulos contribuidos.

Las traducciones de todas las versiones de Drupal se gestionan desde


[Link]. Aunque no es obligatorio registrarse en el sitio para descargar
las traducciones del núcleo y de los módulos contribuidos, al registrarnos y unirnos
a un grupo de traducción, se habilitarán nuevas opciones de importación y
exportación de traducciones.

El acceso a [Link] se realiza con el mismo usuario de [Link]. Una


vez creada la cuenta, podremos unirnos al grupo de traducción de cualquier idioma,
buscando el botón Join en la página principal del idioma [F40.5a].

F40.5a
Página de traducción
de un idioma
Desde la página de
traducción de un idioma
podemos unirnos a un
grupo de traducción,
haciendo clic en el botón
Join. Para poder unirnos
debemos estar registrados
en el sitio, teniendo en
cuenta que se utiliza el
mismo usuario de
[Link].

Una vez unidos a un grupo de traducción, podremos ver las pestañas Translate,
Import y Export [F40.5b].

F40.5b
Pestañas Translate,
Import y Export en
[Link]
Una vez que nos hayamos
registrado y unido al grupo
de traducción de un
idioma, podremos acceder
a las pestañas Translate,
Import y Export.

432 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Desde la pestaña Translate podremos colaborar añadiendo traducciones del


núcleo o de los módulos contribuidos. Las traducciones añadidas pasarán a una
cola de traducción, siendo los moderadores de la traducción los encargados de
revisarlas y aprobarlas. Sólo cuando hayan sido aprobadas, las traducciones
añadidas estarán disponibles para su descarga en el archivo .po correspondiente.

Disponemos de un completo buscador para localizar las cadenas a traducir, por


ejemplo, las pertenecientes a un módulo en concreto. Haciendo clic en Reveal
more filters se nos mostrarán más opciones de búsqueda [F40.5c].

F40.5c
Pestaña Translate
Desde la pestaña Translate
podemos colaborar
incorporando sugerencias
de traducción.
Las traducciones
incorporadas tendrán que
ser validadas por un
moderador, antes de pasar
a formar parte de la
traducción del núcleo o
módulo.

Desde la pestaña Import podremos importar un archivo .po al proyecto. De esta


manera se añadirán como sugerencias todas las traducciones nuevas que hayamos
incorporado en este archivo. Esto puede ser útil si hemos traducido cadenas en
nuestro sitio, mediante la herramienta Traducir interfaz, y queremos colaborar
con el proyecto añadiéndolas en lote como sugerencias de traducción [F40.5d].

F40.5d
Pestaña Import
Desde la pestaña Import
podemos contribuir con la
traducción de módulos
subiendo lotes de
traducción en archivos .po.

Accediendo a la pestaña Export podremos seleccionar el proyecto y versión para


el que queremos obtener la última traducción disponible, que obtendremos en un
archivo .po. Si seleccionamos el proyecto Drupal core podremos descargar la
última versión de la traducción para el núcleo del sistema.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 433
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

Unidad 40. Compartir en comunidad

Lo realmente interesante de este método es que podemos descargar las


sugerencias de traducción, que son las traducciones que aún no han sido
aprobadas por un moderador. Esto nos permitirá tener una traducción más
completa (en cantidad de textos traducidos). Por contra, podría contener errores
de traducción [F40.5e].

F40.5e
Pestaña Export
En la pestaña Export
podemos descargar un
archivo .po con las últimas
traducciones disponibles
de cualquier proyecto.
Como opción adicional
podemos añadir al archivo
las sugerencias de
traducción pendientes de
aprobación.

Solicitar permisos de moderación

Cada grupo tiene sus propias normas para moderar la traducción. Los moderadores
son fundamentales para revisar y aprobar las traducciones enviadas por otros
usuarios. Si quieres participar como moderador de las traducciones, debes
solicitarlo a cualquiera de los administradores del grupo. En el grupo de
traducciones de español puedes escribir un comentario en esta página
solicitándolo:

[Link]

434 Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774
EBOD8BD300055495000774

X Diario de cambios (Change log)


Ayúdanos a mejorar los libros reportando cualquier error que encuentres. Puedes
hacerlo directamente completando este formulario (sólo para usuarios registrados
en [Link]):

[Link]

Versión 1.1 (3/10/2017).

- Apartado 31.1. Corregidos nombres de métodos en MessageInterface.


- Apartado 31.2. Añadida dependencia "enforced" para que las
configuraciones se eliminen completamente al desinstalar el módulo.
- Varias correcciones de estilo, maquetación y errores tipográficos.

Versión 1.0 (17/04/2017). Lanzamiento.

Aprende Drupal con Forcontu | Experto en Drupal 8 Back-End Development III 435
Copyright 2011-2017 Forcontu S.L. Todos los derechos reservados. Queda totalmente prohibida su reproducción y difusión.
Copia de uso exclusivo para LUDWRING ANTHONY LICCIEN AZOCAR con Cédula de identidad (CI) 12762471 Código de verificación:
Powered by TCPDF ([Link]) EBOD8BD300055495000774

También podría gustarte