Slide 1

Slide 1 text

INTRODUCCIÓN AL BACKEND DE DRUPAL (WORKSHOP) Zaragoza Drupal Day Spain 2019 David Rodríguez @davidjguru 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

Slide 2

Slide 2 text

BIENVENIDA David Rodríguez Vicente (Drupal) Center Leader Specialist en Everis Sevilla Responsable técnico de Proyectos basados en Drupal Director técnico del equipo Drupal de Everis Center Sevilla Profile https://www.linkedin.com/in/davidjguru https://www.drupal.org/u/davidjguru Blogs https://medium.com/@davidjguru (esp) https://davidjguru.github.io (en) Repos https://gitlab.com/davidjguru https://github.com/davidjguru Contacto https://twitter.com/davidjguru [email protected] https://twitter.com/DrupalSevilla HELLO MY NAME IS. 1, HOLA QUE TAL 2

Slide 3

Slide 3 text

Ver Drupal por dentro Introducción a tu nuevo backend favorito Progresivo, iterativo e incremental Asentemos conceptos básicos Observa las posibilidades Experiencia Inicial Motivadora Cogiendo cariño a Drupal PRESENTACIÓN 2, dE QUÉ VA ESTO picture from Unsplash, user Konstantin Aal - @kostic86

Slide 4

Slide 4 text

Practicaremos creando nuevos módulos Custom de Drupal. Expondremos los fundamentos del sistema de rutas y controladores de Drupal con la idea de construir accesos a nuestros formularios. Conoceremos los fundamentos de la creación de formularios con Drupal, crearemos varios tanto a mano como con su versión rápida usando Drupal Console. Dotaremos a los campos de formulario con valores ya cargados previamente, y para ese pre-filling de campo usaremos algunos servicios proporcionados por Drupal. Usaremos servicios web para realizar peticiones externas vía REST y cargar información en nuestros formularios. Realizaremos consultas a la base de datos y cargaremos valores dinámicamente en nuestro formulario. PRESENTACIÓN 2, qUÉ VAMOS A HACER

Slide 5

Slide 5 text

MÓDULOS CUSTOM EN DRUPAL RUTAS Y CONTROLADORES EN DRUPAL PERMISOS Y MENÚS EN DRUPAL FORMULARIOS EN DRUPAL COMANDOS ÚTILES EN DRUPAL INYECCIÓN DE DEPENDENCIAS EN DRUPAL PREFILLING DE VALORES EN CAMPOS BIENVENIDA PRESENTACIÓN LECTURAS RECOMENDADAS DESPEDIDA Y CIERRE PRESENTACIÓN CONSULTAS DINÁMICAS A LA BASE DE DATOS 2, QUÉ VA A PASAR 5

Slide 6

Slide 6 text

MÓDULOS CUSTOM EN DRUPAL 3, QUÉ ES UN MÓDULO 6 1- Elegir un nombre. 2- Elegir un nombre corto, “machine_name”. 3- Será usado en la creación de diversos ficheros internos. 4- Servirá para identificar el módulo por parte del sistema Drupal. 5- Debe empezar con una letra. 6- Debe contener solo letras en minúscula y guiones bajos. 7- Debe ser único y no puede coincidir con otro módulo, theme o profile. 8- No debe contener palabras reservadas: lib, js, src, css, vendor, files, assets, images, templates, includes, Drupal.

Slide 7

Slide 7 text

MÓDULOS CUSTOM EN DRUPAL 3, CREAR UN MÓDULO /MODULES/CUSTOM/WORKSHOP_FORMS/ WORKSHOP_FORMS.INFO.YML name: 'Workshop Forms' type: module description: 'My Custom Module for Forms.' core: 8.x package: 'Workshop' 7

Slide 8

Slide 8 text

RUTAS Y CONTROLADORES EN DRUPAL 4, ESO DE ROUTING RUTA CONTROLADOR Esquema de definición de una ruta: Nombre de sistema (machine_name) Ruta física (path) Controladores: clase y método responsables Título Especificación de permisos Nos referimos en realidad a una clase que se hace responsable de gestionar el procesamiento de una ruta. Forma Class::method _controller: \Drupal\[module_name]\Controller\[ClassName]::[method] Forma Service:method _controller: ‘[name_service].controller:[method]’ Forma ClassFormulario _form: ‘\Drupal\[module_name]\Form\[FormClassName]’ Una ruta es (conceptualmente) el camino a seguir para alcanzar un destino a partir de un punto de partida. En Drupal se sigue el modelo de Symfony para gestionar rutas, basado en MVC, donde se definen rutas en un fichero descriptivo y se les asigna una clase responsable (Controlador, Formulario, Servicio). 8

Slide 9

Slide 9 text

RUTAS Y CONTROLADORES EN DRUPAL 4, CREAR EL ROUTING /MODULES/CUSTOM/WORKSHOP_FORMS/ WORKSHOP_FORMS.ROUTING.YML workshop_forms.form: path: '/workshop/forms/form' defaults: _form: '\Drupal\workshop_forms\Form\CustomForm’ _title: 'Workshop Forms Custom Form' requirements: _permission: 'workshop forms access' 9

Slide 10

Slide 10 text

workshop_forms.routing.yml workshop_forms.page: path: '/workshop/forms/page' defaults: _controller: '\Drupal\workshop_forms\Controller\WorkshopFormsPageController::hello' _title: 'Workshop Forms Page' requirements: _permission: 'access content' RUTAS Y CONTROLADORES EN DRUPAL 10 4, si qUIERES JUGAR Puedes añadir más rutas al fichero Pero recuerda construir un controlador y un método en /src/Controller/

Slide 11

Slide 11 text

PERMISOS Y MENÚS EN DRUPAL 5, DEFINIR PERMISOS /MODULES/CUSTOM/WORKSHOP_FORMS/ WORKSHOP_FORMS.PERMISSIONS.YML Workshop forms access: title: 'Workshop forms access' description: 'Access to Workshop form' 11 En Drupal el acceso se puede gestionar a través de permisos, establecidos en un fichero declarativo y luego vinculados a un rol o a un usuario específico.

Slide 12

Slide 12 text

PERMISOS Y MENÚS EN DRUPAL 12 5, CONSTRUIR MENÚ /MODULES/CUSTOM/WORKSHOP_FORMS/ WORKSHOP_FORMS.LINKS.MENU.YML workshop_forms.admin_workshop: title: ‘Workshop’ parent: system.admin route_name: workshop_forms.admin_workshop description: ‘Links to resources from Workshop’ weight: -11 En Drupal 8 la especificación de una ruta se mantiene desacoplada de la definición de un enlace de menú. Los gestionamos como configuración en un fichero propio.

Slide 13

Slide 13 text

PERMISOS Y MENÚS EN DRUPAL 5, ELEMENTOS HIJOS workshop_forms.forms_view: title: 'Workshop Form' parent: workshop_forms.admin_workshop route_name: workshop_forms.form description: 'Showing Workshop Form' weight: 1 workshop_forms.forms_page: title: 'Workshop Page' parent: workshop_forms.admin_workshop route_name: workshop_forms.page description: 'Showing Workshop Page' weight: 2 SI QUIERES AMPLIAR CON OTRO ENLACE HIJO PERO RECUERDA DEFINIR RUTA Y CONTROLADOR 13

Slide 14

Slide 14 text

PERMISOS Y MENÚS EN DRUPAL 4, AMPLIAMOS ROUTING DEFINIMOS UNA RUTA MADRE WORKSHOP_FORMS.ROUTING.YML workshop_forms.admin_workshop path: '/admin/workshop' defaults: _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage' _title: 'Workshop First Link' requirements: _access: 'TRUE' 14

Slide 15

Slide 15 text

FORMULARIOS EN DRUPAL 6, FORM API DE DRUPAL CONCEPTOS BÁSICOS DE FORMULARIOS TIPOS DE FORMULARIOS EN DRUPAL 1- Basic Form: a normal form of general purpose, adaptable. Created from the FormBase Class in Drupal API and maybe the most basic kind of form. Class FormBase.php 2- Config Form: a form of specific use to establish an object and configuration values. Created from the ConfigFormBase in Drupal API. Class ConfigFormBase.php 3- Confirm Form: a form to request confirmation from the user before executing an irreversible action. Created from the ConfigFormBase in Drupal API. Class ConfirmFormBase.php 15

Slide 16

Slide 16 text

FORMULARIOS EN DRUPAL 16 6, OBJETIVO

Slide 17

Slide 17 text

FORMULARIOS EN DRUPAL 6, ELEMENTOS DE FORM textarea, radiobuttons, date, range, number, button, colorpicker, checkbox, range, select.. 17 $form['name'] = [ '#type' => 'textfield', '#title' => $this->t('Name'), '#description' => $this->t('User Name'), '#maxlength' => 64, '#weight' => 0, ]; https://api.drupal.org/api/drupal/elements/8.8.x https://www.drupal.org/docs/8/api/form-api/form-render-elements https://www.drupal.org/docs/develop/user-interface-standards/form-elements

Slide 18

Slide 18 text

FORMULARIOS EN DRUPAL FORMULARIOS EN DRUPAL 6, BASIC FORM DRUPAL /src/Form/CustomForm.php

Slide 19

Slide 19 text

FORMULARIOS EN DRUPAL 6, MÉTODOS BÁSICOS RESPONSABILIDADES CLAVE public function getFormId() { return 'workshop_form'; } public function buildForm(array $form, FormStateInterface $form_state) { return $form; } public function validateForm(array &$form, FormStateInterface $form_state) { } public function submitForm(array &$form, FormStateInterface $form_state) { } Devolver el nombre del formulario -> Construir el formulario y devolverlo -> Validar los valores del formulario -> Enviar y procesar valores del formulario -> $form -> Formulario, $form_state -> Valores 19

Slide 20

Slide 20 text

FORMULARIOS EN DRUPAL 6, CONSTRUCCIÓN 20 $form['name'] = [ '#type' => 'textfield', '#title' => $this->t('Name'), '#description' => $this->t('User Name'), '#maxlength' => 64, '#weight' => 0, ]; $form['id_user'] = [ '#type' => 'number', '#title' => $this->t('User ID'), '#description' => $this->t('User ID'), '#weight' => 1, ]; $form['email'] = [ '#type' => 'email', '#title' => $this->t('Email'), '#description' => $this->t('User email'), '#weight' => 2, ]; $form['number_comments'] = [ '#type' => 'number', '#title' => $this->t('Number of Comments'), '#description' => $this->t('Number of Comments'), '#weight' => 3, ]; $form['types'] = [ '#type' => 'checkboxes', '#title' => $this->t('Content Types'), '#description' => $this->t('Select Content Types'), '#options' => [1 => '1'], '#default_value' => 1, '#weight' => 4, ]; $form['submit'] = [ '#type' => 'submit', '#value' => $this->t('Submit'), '#weight' => 5, ]; return $form; public function buildForm(array $form, FormStateInterface $form_state) { }

Slide 21

Slide 21 text

COMANDOS ÚTILES EN DRUPAL 21 7, ENABLE / DISABLE INSTALAR / DESINSTALAR / LIMPIAR CACHÉ USANDO DRUSH O DRUPAL CONSOLE drush en workshop_forms drush cr drush pmu workshop_forms drupal moi workshop_forms drupal mou workshop_forms

Slide 22

Slide 22 text

INYECCIÓN DE DEPENDENCIAS EN DRUPAL 8, QUÉ ES UN SERVICIO Dependency Injection (DI) es un patrón de diseño orientado a objetos, en el que se suministran objetos a una clase en lugar de ser la propia clase la que cree dichos objetos. Nuestras clases no crean los objetos, sino que se los suministra otra clase ‘contenedora’ que inyectará la implementación deseada. (Wikipedia). Un Service Container (o Dependency Injection container) es un objeto PHP que administra instancias de servicios (otros objetos). Un Servicio, es en sí mismo una clase que cumple una única responsabilidad, generada de manera única para un contexto concreto (Singleton). // Antes: $servicio = new Foo\Meh\Bah; $servicio = Foo\Meh\Bah::getInstance(); 22 https://www.drupal.org/docs/8/api/services-and-dependency-injection https://symfony.com/doc/current/components/dependency_injection.html https://www.drupal.org/docs/8/api/services-and-dependency-injection/dependency-injection-for-a-form // Ahora: $container = $this->getContainer(); $servicio= $container->get('foo.meh.bah');

Slide 23

Slide 23 text

INYECCIÓN DE DEPENDENCIAS EN DRUPAL 8, USO DE SERVICIOS DECLARANDO SERVICIOS services: ban.ip_manager: class: Drupal\ban\BanIpManager arguments: ['@database'] tags: - { name: backend_overridable } ban.middleware: class: Drupal\ban\BanMiddleware arguments: ['@ban.ip_manager'] tags: - { name: http_middleware, priority: 250 } USANDO SERVICIOS DINÁMICAMENTE private $database; private $current_user; public function __construct( Connection $database, AccountProxyInterface $current_user ) { $this->database = $database; $this->current_user = $current_user; } public static function create(ContainerInterface $container) { return new static( $container->get('database'), $container->get('current_user'), ); } USANDO SERVICIOS ESTÁTICAMENTE \Drupal::service('module_installer')->uninstall(['action']); \Drupal::messenger()->addMessage(‘Message to print’); $user = \Drupal::currentUser(); 23 SERVICIOS DISPONIBLE EN CORE CORE.SERVICES.YML

Slide 24

Slide 24 text

$form['name'] = [ '#type' => 'textfield', '#value' => $this->current_user->getDisplayName(), '#title' => $this->t('Name'), '#description' => $this->t('User Name'), '#maxlength' => 64, '#weight' => 0, ]; $form['id_user'] = [ '#type' => 'number', '#value' => $this->current_user->id(), '#title' => $this->t('User ID'), '#description' => $this->t('User ID'), '#maxlength' => 64, '#weight' => '1', ]; $form['email'] = [ '#type' => 'email', '#value' => $this->current_user->getEmail(), '#title' => $this->t('Email'), '#description' => $this->t('User email'), '#weight' => '2', ]; PREFILLING DE VALORES EN CAMPOS 24 9, PRECARGA EN CAMPO USAMOS LOS SERVICIOS INYECTADOS PARA COMPLETAR #VALUE

Slide 25

Slide 25 text

public function validateForm(array &$form, FormStateInterface $form_state) { // Get the email value from the field. $mail = $form_state->getValue('email'); // Test the format of the email. if(!$this->email_validator->isValid($mail)) { $form_state->setErrorByName('email', $this->t('The %email is not valid email.', ['%email' => $mail])); } } PREFILLING DE VALORES EN CAMPOS 25 9, USOS DE SERVICIOS ADEMÁS DE CAMPOS DE FORMULARIO PODEMOS USARLOS EN TODA LA CLASE

Slide 26

Slide 26 text

composer require drupal/devel exec drush en devel devel_generate exec drush genc 10 5 --types=article CONSULTAS DINÁMICAS A LA BASE DE DATOS 26 10, PREPARANDO NODOS INSTALAMOS Y HABILITAMOS DEVEL GENERAMOS CONTENIDO

Slide 27

Slide 27 text

// Build the base query. $query = $this->database->select('comment_field_data', 'c') ->fields('c') ->condition('c.uid', $this->currentUser->id(), '='); // Get the number of registers. $query_counter = $query->countQuery(); $result = $query_counter->execute(); $count = $result->fetchField(); // Using the count value for prefilling the value. $form['number_comments'] = [ '#type' => 'number', '#value' => $count, '#title' => $this->t('Number of Comments'), '#description' => $this->t('Number of Coments'), '#weight' => 3, ]; CONSULTAS DINÁMICAS A BASE DE DATOS 27 10, CONSULTA Y CARGA EN DRUPAL PODEMOS USAR CONSULTAS ESTÁTICAS O DINÁMICAS // Consultas estáticas. $database = \Drupal::database(); $query = $database->query("SELECT id, example FROM {mytable}"); $result = $query->fetchAll(); https://www.drupal.org/docs/8/api/datab ase-api/static-queries

Slide 28

Slide 28 text

$options = node_type_get_names(); $defaults = array_keys($options); $form['types'] = [ '#type' => 'checkboxes', '#title' => $this->t('Content Types'), '#description' => $this->t('Select Content Types'), '#options' => $options, '#default_value' => $defaults, '#weight' => '4', ]; PREFILLING DE VALORES EN CAMPOS 28 9, OTROS ORÍGENES Hemos visto cómo precargar valores en campos a lo largo de diversos ejemplos para nuestros campos de formulario basado en la Form API de Drupal: hemos usado servicios bajo el modelo de Inyección de Dependencias (DI) de Symfony y también hemos realizado consultas directas a la base de datos través de la Database API de Drupal. Ahora veremos otro origen de datos más a partir del que podemos extraer información: simples funciones pre-existentes.

Slide 29

Slide 29 text

FORMULARIOS EN DRUPAL 29 6, ALTERANDO EL FORM if(!$this->current_user->isAuthenticated()) { $form['help'] = [ '#type' => 'item', '#title' => $this->t('Please, read the conditions.'), '#markup' => $this->t('Only for registered users.'), ]; }else { $form['email'] = [ '#type' => 'email', '#value' => $this->current_user->getEmail(), '#title' => $this->t('Email'), '#description' => $this->t('User email'), '#weight' => '2', ]; $form['submit'] = [ '#type' => 'submit', '#value' => $this->t('Submit'), '#weight' => 5, ]; } No debemos perder de vista que en realidad, nunca nos hemos movido del contexto propio de una Clase PHP y un método buildForm(). Así que vamos a jugar con algunas cláusulas if - else.

Slide 30

Slide 30 text

FORMULARIOS EN DRUPAL 30 6, RESULTADOS

Slide 31

Slide 31 text

INYECCIÓN DE DEPENDENCIAS EN DRUPAL 31 public function validateForm(array &$form, FormStateInterface $form_state) { // Get the email value from the field. $mail = $form_state->getValue('email'); // Test the format of the email. if(!$this->email_validator->isValid($mail)) { $form_state->setErrorByName('email', $this->t('The %email is not a valid email.', ['%email' => $mail])); } } public function submitForm(array &$form, FormStateInterface $form_state) { // Display result in submit. foreach ($form_state->getValues() as $key => $value) { \Drupal::messenger()->addMessage($key . ': ' . ($key === 'text_format'?$value['value']:$value)); } } INYECCIÓN DE DEPENDENCIAS EN DRUPAL 8, VALIDACIÓN GLOBAL USAR SERVICIOS PARA VALIDACIONES EN MÉTODOS VALIDATEFORM O SUBMIT

Slide 32

Slide 32 text

$form['titulo'] = [ '#type' => 'textfield', '#title' => $this->t('Título'), '#description' => $this->t('Titulo en el formulario.'), '#required' => TRUE, '#weight' => '0', '#element_validate' => ['::titleCustomValidation'], ]; public function titleCustomValidation($element, FormStateInterface $form_state) { // Insert new custom conditions for valitions in the ‘titulo’ field. $form_state->setErrorByName('titulo', $this->t($error_message)); } PREFILLING DE VALORES EN CAMPOS 32 INYECCIÓN DE DEPENDENCIAS EN DRUPAL INYECCIÓN DE DEPENDENCIAS EN DRUPAL 8, VALIDACIÓN CUSTOM ES POSIBLE USAR VALIDACIONES CUSTOM ESPECÍFICAS PARA CADA CAMPO DE FORM

Slide 33

Slide 33 text

Modo interactivo drupal generate:module drupal gm COMANDOS ÚTILES EN DRUPAL 33 7, CREAR MÓDULOS CREACIÓN AUTOMÁTICA USANDO DRUPAL CONSOLE Modo no interactivo drupal generate:module \ --module="My Random module" \ --machine-name="my_random_module" \ --module-path="modules/custom" \ --description="Random generated custom module" \ --core="8.x" \ --package="Custom" \ --module-file \ --no-interaction

Slide 34

Slide 34 text

Modo interactivo drupal generate:form drupal gf Modo no interactivo drupal generate:form \ --module="my_random_module" \ --class="RandomClassForm" \ --form-id="default_random_form" \ --config-file \ --inputs='"name":"name", "type":"textfield", "label":"Name", "options":"", "description":"User Name", "maxlength":"64", "size":"", "default_value":"", "weight":"0", "fieldset":""' \ --inputs='"name":"id_user", "type":"number", "label":"User ID", "options":"", "description":"User ID", "maxlength":"64", "size":"", "default_value":"", "weight":"1", "fieldset":""' \ --inputs='"name":"email", "type":"email", "label":"Email", "options":"", "description":"User email", "maxlength":"", "size":"", "default_value":"", "weight":"2", "fieldset":""' \ --inputs='"name":"number_comments", "type":"number", "label":"Number of Comments", "options":"", "description":"Number of Coments", "maxlength":"", "size":"", "default_value":"", "weight":"3", "fieldset":""' \ --inputs='"name":"types", "type":"checkboxes", "label":"Content Types", "options":"['1' => '1']", "description":"Select Content Types", "maxlength":"", "size":"", "default_value":"1", "weight":"4", "fieldset":""' \ --path="/my_random_module/forms/random_form" \ --no-interaction COMANDOS ÚTILES EN DRUPAL 34 7, CREAR FORMULARIOS

Slide 35

Slide 35 text

drupal generate:form \ --module="my_random_module" \ --class="RandomClassForm" \ --form-id="default_random_form" \ --config-file \ --inputs='"name":"name", "type":"textfield", "label":"Name", "options":"", "description":"User Name", "maxlength":"64", "size":"", "default_value":"", "weight":"0", "fieldset":""' \ --inputs='"name":"id_user", "type":"number", "label":"User ID", "options":"", "description":"User ID", "maxlength":"64", "size":"", "default_value":"", "weight":"1", "fieldset":""' \ --inputs='"name":"email", "type":"email", "label":"Email", "options":"", "description":"User email", "maxlength":"", "size":"", "default_value":"", "weight":"2", "fieldset":""' \ --inputs='"name":"number_comments", "type":"number", "label":"Number of Comments", "options":"", "description":"Number of Coments", "maxlength":"", "size":"", "default_value":"", "weight":"3", "fieldset":""' \ --inputs='"name":"types", "type":"checkboxes", "label":"Content Types", "options":"['1' => '1']", "description":"Select Content Types", "maxlength":"", "size":"", "default_value":"1", "weight":"4", "fieldset":""' \ --path="/my_random_module/forms/random_form" \ --services="database" \ --services="current_user" \ --services="email.validator" \ --no-interaction COMANDOS ÚTILES EN DRUPAL 35 7, FORMS + SERVICIOS

Slide 36

Slide 36 text

“Hacia rutas salvajes” (Routing en Drupal 8) by @jansete https://docs.google.com/presentation/d/17KVjfmgMtIIIDl0N40Vm1iyaUBg27mGE1hV2byIR4OM/edit “Structure of routes” (Drupal 8 documentation) https://www.drupal.org/docs/8/api/routing-system/structure-of-routes Let’s take the best route - Exploring Drupal 8 routing system https://www.srijan.net/blog/exploring-drupal-8-routing-system Using Links in Drupal 8 https://davidjguru.github.io/blog/drupal-fast-tips-using-links-in-drupal-8 Formularios en Drupal 8 (Crear formularios, modificar formularios, ejemplos con Hooks) https://medium.com/drupal-y-yo/form-api-i-comprender-crear-y-modificar-formularios-en-drupal-8-22c0cf4d72d3 https://medium.com/drupal-y-yo/form-api-ii-modificando-formularios-en-drupal-8-mediante-form-alter-78fa949b43d0 https://medium.com/drupal-y-yo/form-api-iii-caso-pr%C3%A1ctico-de-modificaci%C3%B3n-de-formulario-en-drupal-8-f8ab4dd40a5c Prefilling fields in forms (Precargando campos en formularios) https://davidjguru.github.io/blog/drupal-fast-tips-prefilling-fields-in-forms LECTURAS RECOMENDADAS 36 11, AMPLIA INFORMACIÓN

Slide 37

Slide 37 text

https://gitlab.com/davidjguru/drupal-custom-modules-examples/tree/master/my_random_module DESPEDIDA Y CIERRE 37 12, EVERY TIME WE SAY GOODBYE Recuerda que puedes escribirme a [email protected] y aquí tienes el código disponible: In memory of En recuerdo de