D8 Formularios Avanzado - pierregermain/MyDrupal GitHub Wiki

Páginas relacionadas:

Índice:

Formularios de configuración

Formularios normales heredan de FormBase, los de configuración heredan de ConfigFormBase que nos permite disponer del objeto $config de forma fácil.

Pasos para generar un formulario de configuración

Crear route

my_module_forms.settings:
  path: '/admin/config/development/my_module'
  defaults:
    _form: '\Drupal\my_module_forms\Form\my_moduleSettingsForm'
    _title: 'my_module Forms'
  requirements:
    _permission: 'my_module form access'

Crear menú


my_module_forms.settings:
  title: my_module Forms Settings
  route_name: my_module_forms.settings
  parent: system.admin_config_development
  description: 'Configuration form example.'

Definir permiso

my_module form access:
  title: 'my_module form access'
  description: 'Access form'

Enlace de configuración

Añadimos lo siguiente al *.info.yml de nuestro módulo para poder tener un link de "configure" en el listado de los módulos.

configure: my_module_forms.settings

Crear el fichero de configuración

message: 'Default message'
allowed_types:
- page
- article

Crear la clase

Consideraciones importantes:

  • Hay que definir getEditableConfigNames() para devolver el nombre del fichero de configuración

  • Para escribir/leer configuraciones usamos:

  • En los nombres de elementos del formulario, añadiremos como prefijo el nombre del módulo

    $config = $this->config('my_module_forms.settings');
    $config->get('allowed_types');
    $config->set('allowed_types', $allowed_types);
    
  • no es necesario añadir el botón de Sumit.

<?php
namespace Drupal\my_module_forms\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
class my_moduleSettingsForm extends ConfigFormBase {
  public function getFormId() {
    return 'my_module_forms_admin_settings';
  }
  protected function getEditableConfigNames() {
    return [
      'my_module_forms.settings'
    ];
  }
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('my_module_forms.settings');
    // Listado con todos los tipos de contenido
    $types = node_type_get_names();
    $form['my_module_forms_allowed_types'] = [
      '#type' => 'checkboxes',
      '#title' => $this->t('Content types allowed'),
      '#default_value' => $config->get('allowed_types'),
      '#options' => $types,
      '#description' => $this->t('Select content types.'),
      '#required' => TRUE,
    ];
    $form['my_module_forms_message'] = [
      '#type' => 'textarea',
      '#title' => t('Message'),
      '#cols' => 60,
      '#rows' => 5,
      '#default_value' => $config->get('message'),
    ];
    return parent::buildForm($form, $form_state);
  }
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);
  }
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $allowed_types =
      array_filter($form_state->getValue('my_module_forms_allowed_types'));
    sort($allowed_types);
    $this->config('my_module_forms.settings')
      ->set('allowed_types', $allowed_types)
      ->set('message', $form_state->getValue('my_module_forms_message'))
      ->save();
    parent::submitForm($form, $form_state);
  }
}

Mofificar otros Formularios

Averiguar el form_id

/**
* Implements hook_form_alter.
*/
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  dpm($form_id);
  // Nos da el id para usar el hook siguiente
}

Modificación del formulario (sólo para un formulario concreto)

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_forms_form_system_site_information_settings_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  dpm($form);
  $form['site_information']['site_slogan']['#required'] = TRUE;
}

Modificación de varios formularios distintos a la vez

function my_module_form_alter(&$form,
                                   \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  switch($form_id){
    case 'system_site_information_settings':
      $form['site_information']['site_slogan']['#required'] = TRUE;
      break;
    case 'node_page_form':
    case 'node_page_edit_form':
      // Abre el grupo de opciones
      $form['options']['#open'] = TRUE;
      // Añadir un nuevo elemento llamado highlighted
      $form['options']['highlighted'] = [
        '#type' => 'checkbox',
        '#title' => t('Highlighted'),
        '#weight' => 100,
      ];
      break;
  }
}

Modificación de formularios que usen el mismo BASE_FORM

Primero averiguamos el BASE FORM desde el form_alter.

$form_state->getBuildInfo()['base_form_id'];
// Para nodos por ejemplo nos da
// "node_form"

Una vez averiguado usamos el hook_form_BASE_FORM_ID_alter

/**
 * Implements hook_form_BASE_FORM_ID_alter() for node_form.
 */
function my_module_form_node_form_alter(&$form,
                                             \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['options']['#open'] = TRUE;
  $form['options']['highlighted'] = [
    '#type' => 'checkbox',
    '#title' => t('Highlighted'),
    '#weight' => 100,
  ];
}

Propiedades de Formularios

Estas propiedades se aplican directamente al formulario (array $form).

#type

siempre es form

#form_id

es el id del form

#title

#method

indica el método del submit del form, por defecto es "post".

#attributes

$form['#attributes']['class']= ['node-article-form', 'node-form'];
$form['#attributes']['style'] = 'color:#DDD; font-size: 12px;';

#validate, #submit

Referenciar validate y submit iterno de una clase

$form['#validate'][] = '::validateForm';
$form['#submit'][] = '::submitForm';

Referenciar validate y submit custom interno de la clase

public function buildForm(array $form, FormStateInterface $form_state) {
  $form['#validate'][] = '::customValidateForm';
  $form['#submit'][] = '::customSubmitForm';
  // ...
}
public function customValidateForm(array &$form,
                                   FormStateInterface $form_state) {
  // ...
}
public function customSubmitForm(array &$form,
                                 FormStateInterface $form_state) {
  // ...
}

Referenciar clase externa

$form['#submit'][] = 'contact_form_user_admin_settings_submit';

#element_validate

  • No es una propiedad de $form sino de un elemento del $form.
  • Sirve para validar cada elemento por separado sin usar la función validate.

Referencia hacia funciones externas:

$form['pattern_container']['pattern'] = [
  '#type' => 'textfield',
  '#title' => 'Path pattern',
  '#default_value' => $this->entity->getPattern(),
  '#size' => 65,
  '#maxlength' => 1280,
  '#element_validate' => [
    'token_element_validate',
    'pathauto_pattern_validate'
  ],
];

Referencia hacia funciones internas de la clase:

$form['foo'] = [
  '#title' => 'Test foo',
  '#type' => 'textfield',
  '#element_validate' => ['::elementCustomValidation'],
];

$form['foo'] = [
  '#title' => 'Test foo',
  '#type' => 'textfield',
  '#element_validate' => [
    [get_class($this), 'elementCustomValidation'],
  ],
];

Ahora definimos la función

public function elementCustomValidation($element, FormStateInterface $form_state) {
  if ($element['#value'] == 'invalid') {
    $form_state->setError($element, t('@label element is invalid', ['@label' => $element['#title']]));
  }
}

Añadir botones adicionales tipo submit

(...)

Añadir un valores por defecto a un nodo y guardarlo en DB

Pasos:

  1. Crear tabla en db con un nuevo $schema['nombre_tabla'] que tenga las columnas 'nid' y 'nuevo_campo'
  2. Desde el hook_form_alter obtener el $nid usando $nid = $form_state->getFormObject()->getEntity()->id();
  3. Hacer una query en el hook_form_alter contra la tabla creada y obtener el valor
    $highlighted = $connection->select('my_table', 'f')
    ->fields('f', ['highlighted'])
    ->condition('f.nid', $nid)
    ->execute()
    ->fetchField();
    
  4. Asignar el valor usando '#default_value' => $nuevo_campo,
  5. Crear un nuevo custom submit que meta el valor en la 'nombre_tabla'
  6. Desde el hook_form_alter agregar el nuevo submit usando
$form['actions']['submit']['#submit'][] = 'nombre_modulo_custom_submit';

Control de acceso a elementos de formulario

Dar acceso a un campo según el rol del usuario

// Desde Clase Form POO
$access = $this->currentUser()->hasPermission('my_module form access');
// Desde hook_form_alter
$access = \Drupal::currentUser()->hasPermission('my_module form access');


$form['foo']['#access'] = $access;

Dar acceso a un form según el rol de usuario

Lo hacemos desde el fichero de routing

Ejecutar una validación de acceso a un elemento de un formulario

  • Lo hacemos con la propiedad #access_callback.
  • Sólo se ejecuta si no existe la propiedad #access.
$form['user_email'] = [
  '#type' => 'email',
  '#title' => $this->t('User email'),
  '#description' => $this->t('Your email.'),
  '#access_callback' => ['Drupal\my_module\Form\Simple','checkEmailAccess'],
];

(...)

public function checkEmailAccess($element) {
  $currentUser = \Drupal::currentUser();
  return ($currentUser->id() != 1) &&
  $currentUser->hasPermission('my_module form access');
}

Formulario de confirmación

  • Extienden de ConfirmFormBase
  • Deben implementar los siguientes métodos:
    • getQuestion(). Devuelve la pregunta de confirmación.
    • getConfirmText(). Devuelve el título del botón de confirmación.
    • getCancelText(). Devuelve el título del botón de cancelación.
    • getCancelUrl().

Pasos:

  1. Crear el routing
my_module.confirm:
  path: '/my_module/forms/confirm/{node}'
  defaults:
    _form: '\Drupal\my_module\Form\ConfirmForm'
    _title: 'Confirm Form'
  requirements:
    _permission: 'my_module form access'
  1. Crear la clase
<?php
namespace Drupal\my_module_forms\Form;

use Drupal\Core\Database\Connection;
use Drupal\Core\Url;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class my_moduleConfirmForm extends ConfirmFormBase {
  protected $database;
  protected $node;
  public function __construct(Connection $database) {
    $this->database = $database;
  }
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('database')
    );
  }
  public function getFormId() {
    return 'my_module_forms_confirm';
  }
  public function buildForm(array $form,
                            FormStateInterface $form_state,
                            NodeInterface $node = NULL) {
    $this->node = $node;
    return parent::buildForm($form, $form_state);
  }
  public function getQuestion() {
    return $this->t('Are you sure you want to delete node "%title"
(%nid) from <em>my_module_node_highlighted</em> table?',
      array('%title' => $this->node->getTitle(), '%nid' => $this->node->id()));
}
  public function getConfirmText() {
    return $this->t('Delete');
  }
  public function getCancelText() {
    return $this->t('Don\'t Delete');
  }
  public function getCancelUrl() {
    return new Url('<front>');
  }
  public function submitForm(array &$form,
                             FormStateInterface $form_state) {
    $this->database->delete('my_module_node_highlighted')
      ->condition('nid', $this->node->id())
      ->execute();
    \Drupal::messenger()->addStatus(t('The node has been removed'));
    \Drupal::logger('my_module_forms')->notice(t('Node %nid has been deleted from custom table.'),
      [
        '%nid' => $this->node->id(),
      ]);

    $form_state->setRedirectUrl($this->getCancelUrl());
  }
}


Cargar Formularios sin ruta

Sin Parámetros

$form = \Drupal::formBuilder()->getForm('Drupal\my_module\Form\Simple');

Con Parámetros

$form = \Drupal::formBuilder()
->getForm('Drupal\my_module\Form\ForcontuConfirmForm',
$node);

Métodos disponibles

Inyectar el servicio 'form_builder' en una clase

// ..
public function construct (FormBuilderInterface $form_builder) {
  $this->formBuilder = $form_builder;
}
public static function create(...){
  // ..
  $container->get('form_builder')
  // ..
}
//..
$form =$this->formBuilder->getForm(

Creación de Formularios con drupal console

Formulario de configuración

drupal generate:form:config

Modificar un formulario con hook_form_alter

drupal generate:form:alter

Agregar un Submit y Validate a un Form


<?php

/*
 * Implements hook_form_alter
 */

use Drupal\Core\Form\FormStateInterface;
use Symfony\Components\HttpFoundation\Request;

function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  if ($form_id == 'my_form_id' {
  
    // custom validate
    $form['#validate'][] = 'mymodule_custom_validate';
     
    // custom submit
    $form['actions'][$action]['#submit'][] = 'mymodule_custom_submit';
  }
}

// Validate Form
function mymodule_custom_validate($form, FormStateInterface &$form_state) {
// ...
}

// Submit Form
function mymodule_custom_validate(&$form, FormStateInterface &$form_state) {
// ...
}

Creación de una Tabla con Dropdown de links

  public function messages() {

  //...

    $result = $query->execute();
    $rows = [];


    $build['forcontu_messages_table'] = [
      '#type' => 'table',
      '#header' => ['nid', 'title', 'active', 'message', 'operations','operations POO'],
    ];
    foreach ($result as $record) {

      //dpm($record);

      // Nid y Title
      $nid = $record->nid;
      $node_storage = \Drupal::entityTypeManager()->getStorage('node');
      $node = $node = $node_storage->load($nid);
      $title = $node->get('title')->value;

      $operations = "<a href='/node/".$nid."/edit/'>Edit</a> <a href='/admin/structure/forcontu-pec-message/delete/".$nid."'>Delete</a>";

      $rows[] = [
        $nid,
        $title,
        $record->checked,
        $record->message,
        Markup::create($operations),
        $this->renderDropbutton($nid),
      ];
    }
    $build['forcontu_messages_table']['#rows'] = $rows;
    return $build;
}



public function renderDropbutton($nid) {
    $operations =
     [
      '#type' => 'dropbutton',
      '#links' => [
        'edit' => [
          'title' => 'Edit',
          'url' => Url::fromRoute('entity.node.edit_form', ['node' => $nid]),
        ],
        'delete' => [
          'title' => 'Delete',
          'url' => Url::fromRoute('mymodule.delete', ['node' => $nid]),
        ],
      ],
    ];
    return \Drupal::service('renderer')->render($operations);
}
⚠️ **GitHub.com Fallback** ⚠️