Créer un formulaire de configuration sous Drupal 8

Sous Drupal 7, pour gérer nos variables de configuration système nous utilisions les fonctions variable_get() / variable_set() /  variable_del() et nous enregistrions ces variables dans la table 'variable' de la base de données.

Sous Drupal 8 nous utilisons maintenant le nouveau système de Configuration (Configuration system) qui nous fourni un lieu centralisé pour enregistrer toutes nos donnés de configuration. Ce système nous permet en plus de pouvoir synchroniser  cette information entre nos sites de développement et de production. Ce genre d'information est généralement créé lors du site building et dès lors n'est pas généré par les utilisateurs finaux durant les opérations de routine du site.

Sous Drupal 8 les données de configuration sont toujours enregistrées dans la base de données mais maintenant elles peuvent être aussi enregistrées dans des fichiers YML pour le futur deployment du site.
 
Dans ce billet nous allons créer un formulaire de configuration qui va enregistrer cette information de configuration dans notre système Drupal. Dans notre cas ce sera un formulaire qui enregistrera une clé d'accès à une API externe. Ensuite nous la récupérerons dans un simple contrôleur.

1. Créer le module
2. Créer le formulaire
3. Vérifier, déboguer le formulaire
4. Récupérer l'information dans un  contrôleur.

1. Créer le squelette du module

Pour cela nous allons utiliser la commande de Drupal Console suivante: drupal generate:module

drupal generate:module  \
--module="ex08" \
--machine-name="ex08" \
--module-path="modules/custom" \
--description="An simple example of a config form" \
--core="8.x" \
--package="Custom" \
--module-file \
--uri="http://default" \
--no-interaction

2. Créer le formulaire

Pour créer le formulaire d'enregistrement nous utilisons une fois de plus Drupal Console avec la commande drupal generate:form:config.

drupal generate:form:config  \
--module="ex08" \
--class="ExternalApiKeyForm" \
--form-id="external_api_key_form" \
--config-file \
--inputs='"name":"your_external_api_key", "type":"textfield", "label":"Your external API Key", "options":"", "description":"Enter your external API Key", "maxlength":"64", "size":"64", "default_value":"none", "weight":"0", "fieldset":""' \
--path="/admin/config/ex08/externalapikey" \
--uri="http://default" \
--no-interaction

Cette commande va:

- créer le formulaire de configuration ExternalApiKeyForm.php sous le répertoire: src/form folder (voir le point 3 suivant)
- mettre à jour ou créer le fichier ex08.routing.yml avec une route vers ce formulaire pour pouvoir y accéder (voir le point 4 suivant)

3. Le formulaire de configuration

Si nous ouvrons le fichier ExternalApiKeyForm.php nous pourrons voir le code suivant:

<?php

namespace Drupal\ex08\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Class ExternalApiKeyForm.
 */
class ExternalApiKeyForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'ex08.externalapikey',
    ];
  }

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('ex08.externalapikey');
    $form['your_external_api_key'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Your external API Key'),
      '#description' => $this->t('Store the external API Key'),
      '#maxlength' => 64,
      '#size' => 64,
      '#default_value' => $config->get('your_external_api_key'),
    ];
    return parent::buildForm($form, $form_state);
  }

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

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

    $this->config('ex08.externalapikey')
      ->set('your_external_api_key', $form_state->getValue('your_external_api_key'))
      ->save();
  }

}

Tout d'abord nous voyons que la classe  ExternalApiKeyForm étend la classe abstraite ConfigFormBase qui elle aussi étend la classe abstraite FormBase.

La classe ExternalApiKeyForm dispose de quatre méthodes:

getEditableConfigNames() - définit le nom de l'objet de configuration (Le nom de l'environnement de configuration où nous allons enregistrer ou consulter les variables de configuration). Dans notre cas nous allons l'appeler: 'ex08'.externalapikey'.

getFormId() - définit l'ID unique de notre formulaire. Dans notre cas nous lui donnons l'ID: 'external_api_key_form'.

buildForm() - définit les champs du formulaire. Cette méthode crée le formulaire et les différents champs de ce dernier. Dans notre cas nous avons un seul champ appelé 'your_external_api_key'. Ce champ est un champ de type text. Ce même nom de champ 'your_external_api_key' sera utilisé pour le nom de la variable que nous allons enregistrer dans le système de configuration.

Nous en profitons également pour définir une valeur par défaut pour ce champ:  '#default_value' => $config->get('your_external_api_key'),
 
validateForm() - valide le formulaire.

submitForm() - procède à l'envoi du formulaire. A cette étape nous définissons et sauvons  la variable 'your_external_api_key' dans le système de configuration.

4. La route vers notre formulaire

Rappelez-vous, Drupal Console a aussi mis à jour le fichier ex08.routing.yml avec une route vers notre formulaire pour pouvoir y accéder.

ex08.external_api_key_form:
  path: '/admin/config/ex08/externalapikey'
  defaults:
    _form: '\Drupal\ex08\Form\ExternalApiKeyForm'
    _title: 'ExternalApiKeyForm'
  requirements:
    _permission: 'access administration pages'
  options:
    _admin_route: TRUE

Rien de bien compliqué ici. Avec path nous définissons l'URL pour pouvoir accéder à notre formulaire. Avec _form nous indiquons quelle classe doit générer le formulaire et avec _title nous définissons le titre de la page du formulaire. Enfin avec _permission nous donnons accès au formulaire aux utilisateurs qui ont le droit d'accéder aux pages d'administration. (user access to the administration path).

Maintenant que nous avons un formulaire et une URL nous pouvons accéder au formulaire depuis la URL /admin/config/ex08/externalapikey pour le tester.

Drupal 8 config form

5. Déboguer l'objet configuration.

Nous pouvons aussi déboguer l'objet de configuration avec la commande de Drupal Console debug:config (dc) qui nous donne une liste de tous les noms d'objets de configuration ou aussi un seul objet de configuration. Nous allons d'abord, chercher dans la liste tous les objets qui ont dans leur nom 'ex08' avec l'option grep. Là nous pouvons trouver notre objet 'ex08.externalapikey' et ensuite nous déboguons cet objet en particulier pour voir la valeur qu'il contient.

Drupal console debug config

6. Récupérer la variable dans une classe contrôleur.

Pour pouvoir récupérer ou enregistrer une variable (un objet) de configuration dans une autre classe, nous aurons besoin d'un service. En fait nous aurons besoin du service 'config.factory' du coeur de Drupal.

Nous pouvons trouver ce service avec la commande de Drupal Console: drupal debug:container ou plus précisément drupal debug:container config.factory

Ce service a une méthode qui nous permet de récupérer la valeur d'une variable par son nom: ->get('nom-de-la-variable')

Donc, quand nous allons créer notre contrôleur, nous allons injecter ce service pour pouvoir l'utiliser par la suite.

Nous allons le faire dans ce cas avec Drupal Console.

drupal generate:controller  --module="ex08" \
--class="ExternalApiKeyController" \
--routes='"title":"External API Key", "name":"ex08.external_api_key_controller_ShowKey", "method":"ShowKey", "path":"/ex08/ShowKey"' \
--services='config.factory' \
--uri="http://default" \
--no-interaction

Cette commande crée le fichier ExternalApiKeyController.php dans le répertoire /src/Controller et met à jour le fichier ex08.routing.yml avec une nouvelle route.

Le fichier ex08.routing.yml:

ex08.external_api_key_form:
  path: '/admin/config/ex08/externalapikey'
  defaults:
    _form: '\Drupal\ex08\Form\ExternalApiKeyForm'
    _title: 'ExternalApiKeyForm'
  requirements:
    _permission: 'access administration pages'
  options:
    _admin_route: TRUE

ex08.external_api_key_controller_ShowKey:
  path: '/ex08/ShowKey'
  defaults:
    _controller: '\Drupal\ex08\Controller\ExternalApiKeyController::ShowKey'
    _title: 'External API Key'
  requirements:
    _permission: 'access content'

Le nouveau ExternalApiKeyController.php:

<?php

namespace Drupal\ex08\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\webprofiler\Config\ConfigFactoryWrapper;

/**
 * Class ExternalApiKeyController.
 */
class ExternalApiKeyController extends ControllerBase {

  /**
   * Drupal\webprofiler\Config\ConfigFactoryWrapper definition.
   *
   * @var \Drupal\webprofiler\Config\ConfigFactoryWrapper
   */
  protected $configFactory;

  /**
   * Constructs a new ExternalApiKeyController object.
   */
  public function __construct(ConfigFactoryWrapper $config_factory) {
    $this->configFactory = $config_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('config.factory')
    );
  }

  /**
   * Showkey.
   *
   * @return array
   *
   */
  public function ShowKey() {

    return [
      '#type' => 'markup',
      '#markup' => $this->t('The ShowKey() action')
    ];
  }

}

Nous pouvons voir dans la méthode create() que le service config.factory a été récupérer depuis notre service container et renvoyé vers notre classe elle-même et donc vers la méthode __construct(). Merci Drupal Console!

Maintenant, dans la méthode ShowKey() nous devons accéder à notre objet de configuration et récupérer la valeur de la variable your_external_api_key. C'est ce que nous allons faire dans le code suivant:

  public function ShowKey() {

    // Get the configuration object
    $config = $this->configFactory->get('ex08.externalapikey');
    // Get the value of the key 'your_external_api_key' 
    $key = $config->get('your_external_api_key');

    return [
      '#type' => 'markup',
      '#markup' => $this->t('The External API Key is: @key',['@key'=>$key])
    ];
  }

Et voilà !!!

Récapitulons:

1. Nous avons créé un formulaire de configuration et une route pour y accéder avec la commande DC drupal generate:form:config. Pour cet objet de configuration ('ex08.externalapikey') nous avons défini une variable 'your_external_api_key'.

2. Nous avons débogué cet objet avec la commande DC:  drupal debug:config  <nom-de-object-config>

3. Nous avons créé un contrôleur et injecté le service (config.factory) dont nous avions besoin pour accéder au système de configuration. Dans le contrôleur, nous avons utilisé notre service pour récupérer la valeur d'une variable en particulier de notre objet de configuration.

Il est à noter que nous avons utilisé Drupal Console à chaque étape, vous comprendrez dès lors pourquoi Drupal Console est un 'must' avec Drupal 8.

7. Bonus: Récupérer une variable de configuration dans un hook

Parfois nous avons besoin de récupérer nos variables de configuration dans un hook, dans ce cas il n'est plus possible d'injecter notre service config.factory par dependency injection.

Nous allons utiliser le \Drupal static service container wrapper pour ce faire.

function ex08_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id){
	// Get the config object
	$config = \Drupal::config('ex08.externalapikey');
	// Get the key value
	$key = $config->get('your_external_api_key');
}