Pasar al contenido principal

Formularios con base de datos en Drupal 9

     Si estás leyendo este artículo, es porque ya has aprendido Cómo crear formularios programáticamente en Drupal 9, pero si todavía no has tenido la oportunidad de leer el artículo, además de ver el video en el que te explico cómo hacerlo, te recomiendo que dediques el tiempo que haga falta, para verlos y entenderlo, antes de continuar con el ejercicio que te voy a mostrar a continuación.

     Nos enfocaremos en programar un formulario, conectado a una base de datos, que nos permita, por ejemplo, rellenar los registros de una tabla personalizada, utilizando los datos introducidos por los usuarios, al complementar la información de sus campos .

     También exploraremos la creación de una plantilla o "template" personalizada para el formulario y, de esta forma, podrás modificar su aspecto, en función de los requerimientos que te hagan falta en otros proyectos similares.   

Requisitos:

 

Cómo crear un formulario conectado a una base de datos programáticamente

     Partiendo de que ya tienes los conocimientos necesarios para crear un módulo personalizado y un formulario con sus respectivos campos, nos enfocaremos en continuar ampliando las funcionalidades del formulario base, creando algunos campos adicionales y explorando los atributos, que nos proporcionarán otras opciones con las que podrás ir experimentando a partir de ahora. 

     El formulario que vamos a crear es para la que los usuarios de nuestra web, puedan inscribirse en un curso de cocina vegetariana. La idea principal será utilizar los campos del formulario, para obtener un poco más de información, sobre los interesados en participar, antes de iniciar el curso.

     Puedes aprender sobre los diferentes tipos de campos del formulario, su implementación y algunos ejemplos, visitando la Página oficial de Drupal Form Api.

     Utilizaremos "taxonomías" o vocabularios, con la opción de autocompletar, para filtrar el área profesional de nuestros futuros alumnos y utilizaremos campos dependientes, para añadir preguntas adicionales, en función del campo seleccionado por el interesado, en el momento en que esté complementando el formulario. 

     Paso 1 Creación de un bloque usando un módulo personalizado:

          Como el objetivo es crear un formulario independiente al proyecto, que podamos personalizar y reutilizar en cualquiera de nuestras webs, tendremos que crear un módulo personalizado, donde colocaremos todos los elementos relacionados con él.

          Lo primero que haremos será crear un módulo personalizado, con el cual vamos a generar el bloque, donde colocaremos posteriormente nuestro formulario de inscripción, de esta forma, podremos activarlo en diferentes zonas de la web, además de restringir su acceso definiendo las páginas y usuarios desde la sección de Administración de Bloques.

          Para cumplir con este primer objetivo, vamos a crear las carpetas y archivos necesarios para genera un bloque personalizado, si todavía no sabes cómo, te invito a echar un vistazo antes de continuar a este artículo Cómo crear un bloque programáticamente en Drupal 9

          Una vez hayas terminado, con la creación de todos las carpetas y archivos, necesarios para generar un bloque personalizado, deberías tener una estructura parecida a la de la siguiente imagen.

custom module - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

         Y dentro de la clase con la que vamos a generar nuestro módulo, copiarás y pegarás el siguiente código para comenzar con las pruebas de desarrollo.

<?php

namespace Drupal\vegetarian_food_course\Plugin\Block;

use Drupal\Core\Block\BlockBase;


/**
 * @Block(
 *   id = "Vegetarian Food Course Form",
 *   admin_label = @translation("Vegetarian Food Course Form"),
 *   category = @translation("Vegetarian Food Course Form")
 * )
 */
class VegetarianFoodCourseBlock extends BlockBase {
  /**
   * {@inheritdoc }
   */
  public function build(){
    return [
      '#markup' => '<h1>Formulario de inscripción</h1>',
      '#cache' => [
        'max-age' => 0,
      ],
    ];
  }
}

     Si haz puesto suficiente atención en el proceso, deberías poder activar tu módulo personalizado desde la url "/admin/modules" y a continuación, podrás dirigirte a la pantalla de activación de los bloques, en la url "/admin/structure/block", donde seleccionarás la zona o área Sidebar first, para activar tu módulo y puedas comprobar que se muestra en tu página principal tal y cómo se ve en las siguientes imágenes:

custom module - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

custom block - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     Paso 2 Creación del formulario de inscripción:

          Ahora que ya hemos comprobado que todo funciona de acuerdo con nuestros planes, pasemos a la siguiente etapa, que consiste en la creación de nuestro formulario de inscripción con todos los campos.

          Para ellos, tendremos que crear todas las carpetas y archivos necesarios, si todavía no sabes cómo, te recomiendo que eches un vistazo antes de continuar a este artículo Cómo crear formularios programáticamente en Drupal 9.

custom module - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

         Una vez tengas listos todas las carpetas y archivos necesarios para crear el formulario de inscripción, podrás copiar y pegar en tu clase el código que te muestro a continuación, dentro de la clase de tu formulario:

<?php

namespace Drupal\vegetarian_food_course\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * @file
 * Implement Form API
 */
class VegetarianFoodForm extends FormBase
{

  /**
   * {@inheritdoc }
   */
  public function getFormId()
  {
    return 'vegetarian_food_course_form';
  }

  /**
   * {@inheritdoc }
   */
  public function buildForm(array $form, FormStateInterface $form_state)
  {
    $form['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#description' => $this->t('Name of participant'),
      '#attributes' => [
        'placeholder' => $this->t('Your Name'),
      ],
      '#required' => TRUE,
    ];
    $form['email'] = [
      '#type' => 'email',
      '#title' => $this->t('Email'),
      '#attributes' => [
        'placeholder' => $this->t('Your Email')
      ],
      '#required' => TRUE,
    ];
    $form['vegetarian_question']['active'] = array(
      '#type' => 'radios',
      '#title' => $this
        ->t('Are you Vegetarian?'),
      '#default_value' => 1,
      '#options' => array(
        0 => $this
          ->t('No'),
        1 => $this
          ->t('Yes'),
      ),
    );
//    $form['occupation'] = [
//      '#type' => 'entity_autocomplete',
//      '#target_type' => 'taxonomy_term',
//      '#title' => 'Occupation',
//      '#selection_settings' => [
//        'target_bundles' => ['tags'],
//      ],
//    ];

    $form['actions']['type'] = 'actions';

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Subscribe'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc }
   */
  public function submitForm(array &$form, FormStateInterface $form_state)
  {
    $this->messenger()->addStatus($this->t('Thanks for subscribing @name you will recibe to your email: @email all the information about it',
      ['@name' => $form_state->getValue('name'), '@email' => $form_state->getValue('email')]));

  }
}

    Además, ahora cambiarás el contenido de tu clase VegetarianFoodCourseBlock.php, por el siguiente código, para que en lugar de imprimir un mensaje, como lo hicimos en la primera comprobación, imprima el contenido del formulario que hemos creado.

    Copia y pega el siguiente código, sustituyendo sólo la parte correspondiente a la clase:

class VegetarianFoodCourseBlock extends BlockBase {
  /**
   * {@inheritdoc }
   */
  public function build(){
    return [
      \Drupal::formBuilder()->getForm('\Drupal\vegetarian_food_course\Form\VegetarianFoodForm'),
      '#cache' => [
        'max-age' => 0,
      ],
    ];
  }
}

     Ahora, si vuelves a activar el módulo y el bloque personalizado que creamos anteriormente, podrás ver un formulario con varios campos. Si rellenas los campos y haces clic en el botón Suscribirse, deberías ver en pantalla, un mensaje de confirmación con algunos de los datos que haz introducido.

formulario personalizado - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     Habrás notado, que dentro de la clase hay un campo comentado y por lo tanto no aparece en el formulario, se trata de un campo de tipo autocompletar, que utiliza las taxonomías como opción del formulario, si quisieras activarlo, sólo tendrías que crear previamente algunos términos, dentro del vocabulario al que apunta el campo,  para que se muestren dentro del formulario. 

     Si decides hacer la prueba, antes de continuar, solamente tendrás que descomentar el campo y refrescar el caché de Drupal, recuerda que antes deberías tener al menos un valor para que funcione el autocompletado dentro del campo.

     Paso 3 Creación de una plantilla para el formulario:

          Como nuestra idea es, además de presentar el formulario, imprimir en pantalla los valores que vamos a extraer desde la base de datos, directamente dentro de su mismo bloque y no como un mensaje por defecto en la parte superior de la pantalla, vamos a crear la configuración necesaria, utilizando la función hook_theme(){ }. 

          Si todavía no sabes como utilizarla, te invito a que, antes de continuar, le eches un vistazo al artículo Cómo crear módulos con su Controlador y Plantilla en Drupal 9, donde te explico los principios para que puedas entender lo que sucederá a continuación.

          A continuación, vamos a crear el archivo ".module", donde podremos ejecutar los hooks o funciones php de Drupal, en este caso, la hook_theme(){ }, nos permitirá configurar una plantilla personalizada con la que modificaremos los datos que se presentan dentro del bloque, además de imprimir los valores que obtendremos desde la base de datos un poco más adelante.

         Por ahora, tendrás que crear el archivo vegetarian_food_course.module y dentro copiarás y pegarás el código siguiente, correspondiente a la función y los parámetros que vamos a pasarle desde el bloque.

<?php

/**
 * @file
 * Implements hook_theme()
 */


function vegetarian_food_course_theme($existing, $type, $theme, $path) {
  return [
    'vegetarian-food-course-form' => [
      'variables' => [
        'title' => NULL,
        'records' => NULL,
        'form' => NULL,
      ]

    ]
  ];
}

          Esta función nos permitirá crear una plantilla Twig, que nos devolverá un arreglo o array llamado variables, y dentro del mismo, hemos declarado tres valores nulos, porque los obtendremos directamente desde la clase que genera nuestro bloque.

          Estos tres valores, son:

  • Title: que utilizaremos para reescribir el que se muestra por defecto en nuestro bloque
     
  • Records: que nos permitirá imprimir los valores obtenidos cuando nos conectemos a la base de datos
     
  • Form: que nos permitirá imprimir el formulario y hacer modificaciones en su aspecto.

          La siguiente modificación que tendremos que hacer, será dentro de nuestra clase VegetarianFoodCourseBlock.php, porque vamos a invocar la función hook_theme(){ } y además, añadiremos una variable para el formulario y otra para los valores de la base de datos.

          Copia y sustituye el código que te muestro a continuación en la clase VegetarianFoodCourseBlock.php

class VegetarianFoodCourseBlock extends BlockBase {
  /**
   * {@inheritdoc }
   */
  public function build(){

    $form = \Drupal::formBuilder()->getForm('\Drupal\vegetarian_food_course\Form\VegetarianFoodForm');

    $output = [];

    return [
      '#theme' => 'vegetarian-food-course-form',
      '#title' => $this->t('Formulario de Inscripción'),
      '#records' => $output,
      '#form' => $form,
      '#cache' => [
        'max-age' => 0,
      ],
    ];
  }
}

          Llegados a este punto, tendremos que crear la plantilla twig, con el nombre vegetarian-food-course-form.html.twig, dentro de la carpeta templates, tal y como te muestro en la imagen.

templates - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

          Luego, copia el código que te muestro a continuación, dentro de tu plantilla y una vez realizado este proceso, podrás ver los cambios dentro del bloque.

<style>
  h1.titulo-inscripcion {
    text-align: center;
    font-weight: 600;
    text-decoration: underline;
    margin-bottom: 30px;
  }
  .formulario-inscripcion {
    padding: 20px 10px;
    border: 2px dotted;
    margin-bottom: 20px;
  }
</style>

<h1 class="titulo-inscripcion">
  {{ title }}
</h1>
<div class="formulario-inscripcion">
  {{ form }}
</div>

          Notarás inmediatamente el nuevo aspecto de tu bloque, hemos añadido algunos estilos en la parte superior de la plantilla, para poder modificar el aspecto del los elementos utilizando las clases con las que hemos envuelto el título y el formulario.

custom block - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     Si ves el título del bloque por duplicado, es porque todavía estará marcado dentro de la pantalla de Administración de bloques, una vez lo hayas modificados, si refrescas caché lo verás tal y como se muestra en la imagen.

custom block - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

 

     Paso 4 Creación de la tabla dentro de la base de datos:

          Sientes un gran estado de emoción, por lo lejos que haz llegado en tan poco tiempo, ya conoces la forma de crear un bloque personalizado, un formulario, y además, combinar ambos, para mostrarlos dentro de una plantilla, a la que puedes manipular a tu antojo.

          Así que, sin más dilación, vamos a la parte cumbre de este ejercicio, crearemos la tabla, donde alojaremos todos los datos introducidos a través del formulario, para luego mostrarlos en la plantilla.

          Para lograr nuestro objetivo, crearemos una tabla programáticamente y luego simplemente tendremos que configurar, en el bloque para que los datos que recogeremos se muestren en pantalla.

          Si todavía no haz creado tu primera tabla programáticamente, es el momento de parar para ver el artículo  Cómo crear tablas programáticamente en Drupal 9, donde te lo explico detalladamente.

         Crea un archivo llamado vegetarian_food_course.install y dentro, pega el siguiente código, para que se genere tu nueva tabla, con todos los campos que hemos definido en ella.

<?php
/**
 * Implment hook_schema()
 */
function vegetarian_food_course_schema() {

  $schema['vegetarian_food_course'] = array(
    'description' => 'Save the data of the registrants for the course',
    'fields' => array(
      'vid' => array(
        'type' => 'serial',
        'not null' => TRUE,
        'description' => 'Primary Key: Identificador único del participante.',
      ),
      'name' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Participant name.',
      ),
      'lastname' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Participant\'s last name.',
      ),
      'email' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Participant email.',
      ),
      'vegetarian_question' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Participant email.',
      ),
      'occupation' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
        'description' => 'Participant email.',
      ),
    ),
    'primary key' => array('vid'),
    'indexes' => array(
      'name' => array('name'),
      'lastname' => array('lastname'),
      'email' => array('email'),
      'vegetarian_question' => array('vegetarian_question'),
      'occupation' => array('occupation'),
    ),
  );
  return $schema;
}

custom table - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

          Esta vez, tendrás que desinstalar el módulo y volver a instalarlo, ya que vamos a crear una tabla, en el momento en que realizamos dicha instalación.

          Además, aprovechamos para añadir algunos campos adicionales, como el apellido y descomentamos el autocompletado del formulario, después de añadir algunos términos dentro del vocabulario tag, para que ya puedas ver al completo como sería el funcionamiento real.

          Por lo tanto, para que puedas probarlo sin errores, copia y sustituye el código dentro de la clase del formulario:

<?php

namespace Drupal\vegetarian_food_course\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * @file
 * Implement Form API
 */
class VegetarianFoodForm extends FormBase
{

  /**
   * {@inheritdoc }
   */
  public function getFormId()
  {
    return 'vegetarian_food_course_form';
  }

  /**
   * {@inheritdoc }
   */
  public function buildForm(array $form, FormStateInterface $form_state)
  {
    $form['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#description' => $this->t('Name of participant'),
      '#attributes' => [
        'placeholder' => $this->t('Your Name'),
      ],
      '#required' => TRUE,
    ];
    $form['lastname'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Last Name'),
      '#description' => $this->t('Last Name of participant'),
      '#attributes' => [
        'placeholder' => $this->t('Your Last Name'),
      ],
      '#required' => TRUE,
    ];
    $form['email'] = [
      '#type' => 'email',
      '#title' => $this->t('Email'),
      '#attributes' => [
        'placeholder' => $this->t('Your Email')
      ],
      '#required' => TRUE,
    ];
    $form['vegetarian_question']['active'] = array(
      '#type' => 'radios',
      '#title' => $this
        ->t('Are you Vegetarian?'),
      '#default_value' => 1,
      '#options' => array(
        0 => $this
          ->t('No'),
        1 => $this
          ->t('Yes'),
      ),
    );
    $form['occupation'] = [
      '#type' => 'entity_autocomplete',
      '#target_type' => 'taxonomy_term',
      '#title' => 'Occupation',
      '#selection_settings' => [
        'target_bundles' => ['tags'],
      ],
    ];

    $form['actions']['type'] = 'actions';

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Subscribe'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc }
   */
  public function submitForm(array &$form, FormStateInterface $form_state)
  {
    $this->messenger()->addStatus($this->t('Thanks for subscribing @name you will recibe to your email: @email all the information about it',
      ['@name' => $form_state->getValue('name'), '@email' => $form_state->getValue('email')]));

  }
}

          Y recuerda, que como hemos desinstalado el módulo, tendremos que volver a Administrar bloques para activarlo nuevamente, tomando en cuenta de que hemos modificado su nombre con el hook_theme(){ }, tendremos que buscarlo con el nuevo nombre que hayamos puesto en la variable title.

custom block - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

          Si todo ha ido bien, deberías poder ver un formulario completo, con el nuevo campo autocompletar y el apellido, y al rellenar todos los datos, sería como el que te muestro en la siguiente imagen.

custom form - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     A continuación vamos a realizar los últimos cambios, primero crearemos una consulta dinámica, dentro de nuestro bloque, para poder mostar los datos de la tabla en nuestra plantilla, si quieres conocer un poco más sobre la construcción de queries o consultas en Drupal, tienes la Página oficial

     Copia y pega el código siguiente, dentro de la plantilla que genera nuestro bloque:

class VegetarianFoodCourseBlock extends BlockBase {
  /**
   * {@inheritdoc }
   */
  public function build(){

    $form = \Drupal::formBuilder()->getForm('\Drupal\vegetarian_food_course\Form\VegetarianFoodForm');

    $database = \Drupal::database();
    $query = $database->select('vegetarian_food_course', 'vgc');
    $query->fields('vgc');
    $result = $query->execute()->fetchAll();


    return [
      '#theme' => 'vegetarian-food-course-form',
      '#title' => $this->t('Formulario de Inscripción'),
      '#records' => $result,
      '#form' => $form,
      '#cache' => [
        'max-age' => 0,
      ],
    ];
  }
}

     En esta parte del código, nuestro objetivo es decirle a Drupal que consulte en nuestra tabla vegetarian_food_course, todos sus campos y luego los recorra para poder acceder a ellos e imprimirlo dentro de la plantilla, hemos sustituido el array output por $result, para que tenga más coherencia, pero puedes usar el mismo si lo deseas.

     Otra modificación, está en la función submitForm(){}, para que en lugar de imprimir el mensaje en pantalla, como hacíamos al principio del artículo, ahora, cuando rellenemos los campos del formulario, se guarden dentro de la tabla que hemos creado.

     Copia y sustituye el código dentro de la clase que genera el formulario, para conseguir este cambio de funcionalidad.

  /**
   * {@inheritdoc }
   */
  public function submitForm(array &$form, FormStateInterface $form_state)
  {

    \Drupal::database()->insert('vegetarian_food_course')
      ->fields(['name', 'lastname','email','vegetarian_question','occupation'])
      ->values(array(
        $form_state->getValue('name'),
        $form_state->getValue('lastname'),
        $form_state->getValue('email'),
        $form_state->getValue('vegetarian_question'),
        $form_state->getValue('occupation'),
      ))
      ->execute();

     $this->messenger()->addStatus($this->t('Thanks for subscribing @name you will recibe to your email: @email all the information about it',
   ['@name' => $form_state->getValue('name'), '@email' => $form_state->getValue('email')]));

  }
}

     Ahora que ya hemos realizado los cambios en la clase del bloque y en la función submit, si desinstalas y reinstalas el módulo, al rellenar los campos y darte el mensaje de confirmación como antes, también podrás ver que tus datos han sido añadidos a la tabla correspondiente en nuestra base de datos.

custom block - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     Si abres tu gestor de base de datos, y buscas la tabla relacionada con nuestro módulo, podrás comprobar que todo funciona según lo planeado.

database - www.drupaladicto.org Formacion especializada en drupal y symfony - roles permisos y usuarios

     Añadir traducciones (PLUS):

          Para que nuestro módulo sea completo, pensamos que sería correcto añadirle los archivos de traducción y la configuración adicional, para que al instalarlo, Drupal los reconozca y automáticamente los muestre en el idioma correspondiente.

          Para que podamos añadir archivos de este tipo, primero tendremos que agregar algunas líneas dentro de nuestro archivo .info, y, posteriormente, el archivo que contendrá todas nuestras traducciones.

          Nuestro archivo .info, quedará de la siguiente forma:

name: Vegetarian Food Course
description: 'Create a registration form for the course'
package: drupaladicto

type: module
core_version_requirement: ^8.8.0 || ^9.0

project: vegetarian_food_course
'interface translation project': vegetarian_food_course
'interface translation server pattern': modules/custom/vegetarian_food_course/translations/vegetarian_food_course-%language.po

dependencies:
  - node

     Una vez hayamos añadido la información relacionada con nuestro archivo de traducciones y su ruta, podremos añadir nuestro archivo correspondiente, que para este ejercicio queda de la siguiente forma:

msgid "Name of participant"
msgstr "Nombre del participante"

msgid "Last Name of participant"
msgstr "Apellido del participante"

msgid "Are you Vegetarian?"
msgstr "¿Eres vegetariano?"

msgid "More Vegetarians in your Family?"
msgstr "¿Hay más vegetarianos en tu familia?"

msgid "Other vegetarians"
msgstr "Otros familiares vegetarianos"

msgid "Occupation"
msgstr "Profesión"

msgid "Your Name"
msgstr "Su nombre"

msgid "Your Last Name"
msgstr "Su apellido"

msgid "Your Email"
msgstr "Su correo electrónico"

msgid "1 Person"
msgstr "1 Persona"

msgid "2 Persons"
msgstr "2 Personas"

msgid "3 Persons"
msgstr "3 Personas"

msgid "All my family"
msgstr "Toda mi familia"

     Recuerda, que la ubicación debe corresponder con la que haz declarado en tu archivo .info o no funcionará.