Utiliser des formulaires dans Drupal

Drupal 8

Utiliser des formulaires dans Drupal

Soumis par Antoine le lun 23/03/2020 - 13:30

Drupal dispose d'une api pour gérer les formulaires. La classe form, implémente l'interface \Drupal\Core\Form\FormInterface. Comme pour les render array, le contenu est généré à l'aide tableau associatif. La construction, la validation et les actions à faire après un submit seront stockées dans le fichier src/Form/NomDuFormForm.php. Ensuite, il n'y aura plus qu'à appeler le formulaire depuis le contrôleur et l'afficher dans un gabarit.

Dans l'exemple ci-dessous, nous allons créer un module "masseffect" qui propose un formulaire pour appliquer des actions de masse à certains types de contenu. Par exemple, nous voulons publier ou dépublier tous les articles, ou tous les articles et les pages de base. Nous aurons donc un formulaire composé de 3 champs :

  • Des cases à cocher pour choisir le type de contenu
  • Un bouton radio pour choisir publier ou dépublier
  • Un bouton submit 

Le fichier src/Form/ChooseTypeForm.php

La première chose à faire est de créer le namespace du fichier et d'implanter les classes FormBase et FormstateInterface.

//Fichier src/Form/ChooseTypeForm
<?php
namespace Drupal\masseffect\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

 Ensuite on crée la classe ChooseTypeForm du formulaire qui étend la classe FormBase. Attention, la classe doit porter le même nom que le fichier

class ChooseTypeForm extends FormBase {

}

Cette classe doit obligatoirement contenir 3 méthodes :

  • getFormId() qui renvoie un identifiant unique
  • buildForm(array $form, FormStateInterface $form_state, arg_facultatif) qui sert à construire le formulaire
  • submitForm(array &$form, FormStateInterface $form_state) qui est lancée quand le formulaire est validé

Il est possible de rajouter un méthode validateForm(array &$form, FormStateInterface $form_state) qui permet de fixer des règles de validation du formulaire comme "Le mot de passe doit faire au minimum 8 caractères"

On commence par mettre les squelettes de ces 4 méthodes :

  /**
   * {@inheritdoc}
   */
public function getFormId() {
   
  }

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


  }

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

  }

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

  }

La méthode getFormId() renvoit l'identifiant unique du formulaire. 

public function getFormId() {
    return 'choose_type_form';
  }

La méthode buildForm(array $form, FormStateInterface $form_state, arg_facultatif) doit renvoyer le tableau associatif $form qui va contenir chaque élément du fonctionnaire. La page form and render de la doc officielle liste les éléments qui peuvent être mis dans un formulaire. Dans notre cas, nous allons mettre des cases à cocher pour que l'utilisateur puisse choisir parmi la liste des types de contenu et des boutons radios pour choisir entre publier et dépublier.

 

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

    //Quand on créé des cases à cocher, on créé un tableau checkboxes avec une clé '#options' qui liste les différentes cases

    //Pour générer les options, on récupère les différents types de contenu et on génére un tableau associatif dont les clés sont
    //les identifiant des types, et les valeurs, les noms des types
    //Par exemples Les pages de bases sont stockés $options['page']='Page de base'
    $listOfType=$entitiestypes = \Drupal\node\Entity\NodeType::loadMultiple();
    if ($listOfType!=null){
      foreach ($listOfType as $type){
          $options[$type->id()]=$type->label();
      }

      //On peut créer le champ Checkbox
      $form['type'] = array(
        '#title' => t('Type of content choice'),
        '#type' => 'checkboxes',
        '#options' => $options,
      );
    }

    //On génère les boutons radios. On rajoute la clé #required pour imposer un choix
    $form['publish'] = [
      '#type' => 'radios', //permet d'indiquer que l'on veut un bouton radio
      '#title' => $this->t('Do you want generate auto?'), //Défini le texte affiché au dessus des boutons radios
      '#options' => [
        "1"=>$this->t('Yes'),
        "0"=>$this->t('No')
      ],
      '#required'=>true,

    ];

    //On génère le bouton submit
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
      '#button_type' => 'primary',
    ];

    //On renvoit le formulaire
    return $form;
  }

La méthode validateForm(array &$form, FormStateInterface $form_state) reste vide car dans notre situation nous n'avons rien à valider. Le principe normalement est de tester les valeurs récupérées dans  le form state est de renvoyer une erreur si une règle n'est pas respectée. Le code ci-dessous renvoie une erreur si un champ codepostal fait moins de 5 caractères.

Pour récupérer les valeurs saisie, on utilise la méthode $form_state->getvalue('nom du champ'). Le nom du champ correspond à clé du tableau associatif $form qui a permis de créer chaque champ dans la méthode buildForm(). Dans notre cas, ca pourrait être type ou publish

Pour renvoyer une erreur, on utilise la fonction  $form_state->setErrorByName('champs', $this>-t(message)')

  public function validateForm(array &$form, FormStateInterface $form_state) {
    if (strlen($form_state->getValue('phone_number')) < 3) {
      $form_state->setErrorByName('phone_number', $this->t('The phone number is too short. Please enter a full phone number.'));
    }
  }

La méthode submitForm(array &$form, FormStateInterface $form_state) va récupérer les valeurs saisie dans le formulaire grâce à la méthode $form_state->getvalue('nom du champ').

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

    foreach ($form_state->getValue('type') as $type){
      $entities  = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties(['type' => $type]);
      foreach ($entities as $entitie){
        $entitie->setPublished($form_state->getValue('publish'));
        $entitie->save();
      }
    }

  }

Afficher le formulaire

Pour afficher le formulaire, on peut utiliser deux méthodes. Soit le récupérer dans un contrôleur grâce au service formbuilder(), soit l'afficher directement depuis une route de type form.

L'appel du formulaire dans le contrôleur

Pour récupérer le formulaire on utilise le service $form = \Drupal::formBuilder()->getForm('Drupal\nomdumodule\Form\classeduformulaire'); 

<?php
/**
 * Created by PhpStorm.
 * User: install
 * Date: 19/03/2020
 * Time: 23:11
 */
namespace Drupal\masseffect\Controller;
use Drupal\Core\Controller\ControllerBase;


/**
 * Returns responses for premiermo routes.
 */
class MasseffectController extends ControllerBase
{

  Public function home() {
    $form = \Drupal::formBuilder()->getForm('Drupal\masseffect\Form\ChooseTypeForm');
    $arr['content'] = [
      '#theme' => 'home',
      '#form' => $form,

    ];
    return $arr;
  }
}

Si l'on veut faire passer des arguments au formulaire, on les rajoute après de la classe 

$form = \Drupal::formBuilder()->getForm('Drupal\nomdumodule\Form\classeduformulaire', $arg1, arg2). 

Dans ce cas, la méthode buidForm doit être déclarée avec ces arguments :  

public function buildForm(array $form, FormStateInterface $form_state, $arg1=null, $arg2=null) {
}

L'appel depuis une route

La clé _form: '\Drupal\nommodule\Form\classeduformulaire permet d'afficher le formulaire. Elle doit être placé comme une sous clé de la clé defaults

masseffect.home2:
  path: '/masseffect/home2'
  defaults:
    _form: '\Drupal\masseffect\Form\ChooseTypeForm'
  requirements:
    _permission: 'access content'

Récupérer des informations générées par la méthode submit dans la page qui affiche le formulaire

Imaginons que nous voulions générer un tableau qui liste les entités que nous venons de modifier. Notre problème est que nous pouvons obtenir cette liste que depuis la méthode submit. Dans du php de base, on utiliserait la variable $_POST dans le contrôleur. Malheureusement, Drupal ne permet pas d'utiliser cette méthode. La solution la plus simple consiste à

  • stocker en session notre liste dans la méthode submit,
  • dans le contrôleur tester si la variable de session existe et générer notre contenu si c'est le cas

 

Version