Drupal 8 Workshop: Introducción al Backend de Drupal

Drupal 8 Workshop: Introducción al Backend de Drupal

Introducción al Backend de Drupal es un taller impartido durante el Drupal Day Spain 2019 en Zaragoza, estas son las slides de la actividad.

Incluye aspectos como:
- Creación de módulos custom en Drupal
- Creación de formularios custom en Drupal
- Inyección de Dependencias para Servicios en Drupal
- Prefilling de valores en campos de formulario
- Comandos útiles para herramientas de consola en Drupal

Eb447e62da11f7aecc85bae13e97c2a8?s=128

David Rodríguez (davidjguru)

November 23, 2019
Tweet

Transcript

  1. 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
  2. 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 davidjguru@gmail.com https://twitter.com/DrupalSevilla HELLO MY NAME IS. 1, HOLA QUE TAL 2
  3. 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
  4. 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
  5. 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
  6. 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.
  7. 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
  8. 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
  9. 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
  10. 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/
  11. 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.
  12. 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.
  13. 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
  14. 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
  15. 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
  16. FORMULARIOS EN DRUPAL 16 6, OBJETIVO

  17. 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
  18. FORMULARIOS EN DRUPAL FORMULARIOS EN DRUPAL 6, BASIC FORM DRUPAL

    /src/Form/CustomForm.php <?php namespace Drupal\workshop_forms\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; class CustomForm extends FormBase { } picture from Unsplash, user Mitil - @gigantfotos 18
  19. 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
  20. 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) { }
  21. 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
  22. 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');
  23. 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
  24. $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
  25. 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
  26. 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
  27. // 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
  28. $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.
  29. 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('<strong>Only for registered users.</strong>'), ]; }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.
  30. FORMULARIOS EN DRUPAL 30 6, RESULTADOS

  31. 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
  32. $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
  33. 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
  34. 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
  35. 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
  36. “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
  37. 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 davidjguru@gmail.com y aquí tienes el código disponible: In memory of En recuerdo de