Snippets

Les snippets sont des bout de code.

Présenté ici de manière volontairement « brute », ces snippets ont vocation à servir d'aide mémoire.

N'hésitez-pas à poser un commentaire si vous rencontrez un problème avec un des éléments.

Vous pouvez utiliser la navigation via les tags présents sur la droite

  • Drupal 8 - rendre un formulaire dans un template

    Posté le Mercredi 5 juin 2019 - 09:36
    Dernière mise à jour le Mercredi 5 juin 2019 - 10:48

    La form api de drupal est très puissante, mais pour jouer avec le markup html avec les #prefix et #suffix peut s'avérer rapidement complexe en plus d'être un peu sale au niveau du code.

    Dans drupal 8 il est possible de facilement utiliser un template pour un formulaire. Pour cela il faut se baser sur le nom machine du formulaire.

    Cela fonctionne pour nos formulaires custom, mais aussi pour les formulaires des modules tiers ou du core. Je vais ici utiliser un template pour le formulaire user_form qui correspond au formulaire de modification du compte utilisateur. Le nom machine de ce formulaire est user_form.

    Définition du template :

    Fichier : mon_module.module

    1. function mon_module_theme() {
    2. $theme = [];
    3. $theme['user_form'] = [
    4. 'render element' => 'form',
    5. 'template' => 'user-form',
    6. ];
    7. return $theme;
    8. }

    J'utilise comme clé de thème le nom machine du formulaire.

    Je peux ensuite créer dans le dossier templates de mon thème le fichier user-form.html.twig : (attention au _ remplacé par un -)

    1. <div>
    2. <p>Test</p>
    3. {{ form }}
    4. <div>

    On peut rendre des champs « manuellement », mais il ne faut pas oublier de rendre le formulaire form à la fin : 

    1. <div>
    2. <p>Test</p>
    3. <div class="col-sm-6">
    4. {{ form.mon_champ_1 }}
    5. </div>
    6. <div class="col-sm-6">
    7. {{ form.mon_champ_2 }}
    8. </div>
    9. {{ form | without('mon_champ_1', 'mon_champ_2') }}
    10. <div>

     

  • Drupal 8 - Créer un Event, le lancer et l'intercepter

    Posté le Mardi 4 juin 2019 - 11:55

    Dans Drupal 8, le principe des hooks a été remplacé par un système d'évènement qui est lancé et peut être intercepté, cela se passe en 3 étapes :

    1. Création de l'évènement (classe étendant la classe Event)
    2. Lancer l'évènement
    3. Interception de l'évènement (Classe étendant l'interface EventSubscriberInterface)

    Pour cette évènement, nous allons prendre le contexte de mon site de pronostics sportifs. Je veux lancer un évènement quand un utilisateur fait ses pronostics

    Création de l'évènement

    Fichier : mon_module/src/Event/UserBetEvent.php

    1. <?php
    2.  
    3. namespace Drupal\mon_module\Event;
    4.  
    5. use Drupal\mespronos\Entity\Day;
    6. use Drupal\user\UserInterface;
    7. use Symfony\Component\EventDispatcher\Event;
    8.  
    9. class UserBetEvent extends Event {
    10.  
    11. const EVENT_NAME = 'mespronos_user_bet';
    12.  
    13. /**
    14.   * The user account.
    15.   *
    16.   * @var \Drupal\user\UserInterface
    17.   */
    18. public $account;
    19.  
    20. /**
    21.   * The Day the user has bet on
    22.   *
    23.   * @var Day
    24.   */
    25. public $day;
    26.  
    27. public function __construct(UserInterface $account, Day $day) {
    28. $this->account = $account;
    29. $this->day = $day;
    30. }
    31.  
    32. }

    Cette classe est relativement simple, on défini les attributs que l'on veut rendre disponibles lors de l'interception de cet évènement, ici, l'objet User de l'utilisateur qui aura pronostiqué et la journée ($day) de compétition sur laquelle il aura fait ses pronostics.

    Dispatch de l'évènement

    Le code suivant est à placer là où vous souhaiter lancer l'évènement, dans mon cas il s'agit de la méthode submit de mon formulaire de pronostics :

    1. $event = new UserBetEvent($user, $day);
    2. $event_dispatcher = \Drupal::service('event_dispatcher');
    3. $event_dispatcher->dispatch(UserBetEvent::EVENT_NAME, $event);

    Évidement, il ne faut pas oublier de passer les paramètres que l'on a défini dans le constructeur de notre évènement (ici $user et $day)

    Interception de l'évènement

    Définition du subscriber

    Fichier : mon_autre_module/mon_autre_module.services.yml

    1. mon_autre_module.mespronos_user_bet:
    2.   class: 'Drupal\mon_autre_module\EventSubscriber\UserBetSubscriber'
    3.   tags:
    4.   - { name: 'event_subscriber' }

    Code du subscriber

    Fichier : mon_autre_module/src/EventSubscriber/UserBetSubscriber.php

    1. <?php
    2.  
    3. namespace Drupal\mon_autre_module\EventSubscriber;
    4.  
    5. use Drupal\mespronos\Event\UserBetEvent;
    6. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    7.  
    8. class UserBetSubscriber implements EventSubscriberInterface {
    9.  
    10. public function onBet(UserBetEvent $event) {
    11. // On peut récuperer les attributs de l'évènement
    12. $user = $event->account;
    13. $day = $event->day;
    14.  
    15. // À vous de faire ce que vous voulez ici
    16. }
    17.  
    18. public static function getSubscribedEvents() {
    19. // Définition du ou des évènements que l'on écoute et méthode à executer
    20. return [
    21. UserBetEvent::EVENT_NAME => 'onBet',
    22. ];
    23. }
    24.  
    25. }

     

  • Drupal 8 - rejouer une fonction d'update

    Posté le Mardi 28 mai 2019 - 09:26

    Drupal tient un registre des versions des modules installés, ces versions correspondent au dernier numéro du hook_update exécuté du module.

    Si par exemple vous venez de lancer le hook 8301 du module field_group et que vous souhaitez le relancer, alors il faudra passer le module en version 8300.

    Voila comment faire avec drush :

    1. drush ev "drupal_set_installed_schema_version('field_group', 8300)"

     

  • Drupal 8 - Exemple d'utilisation simple du cache

    Posté le Mercredi 15 mai 2019 - 12:18

    Drupal 8 propose un système de cache très puissant et à plusieurs niveaux.

    Ici nous allons voir comment stocker simplement le résultat d'une requête en cache afin de ne pas avoir à la lancer la requête SQL à chaque appel.

    Commençons par définir notre « conteneur » de cache dans le fichier mon_module.services.yml :

    1. cache.mon_module:
    2.   class: Drupal\Core\Cache\CacheBackendInterface
    3.   tags:
    4.   - { name: cache.bin }
    5.   factory: cache_factory:get
    6.   arguments: [mon_module]

    mon_module sera le nom de notre conteneur de cache.

    Cela permettra de distinguer les données que nous mettrons dedans et de ne pas écraser d'autres caches d'autres modules. Si vous utilisez le cache en base de données (par défaut) vous verrez qu'une nouvelle table cache_mon_module a été créée.

    Ensuite voici un exemple de lecture du cache, et d'écriture si la donnée n'est pas présente :

    1. public function getVersion() {
    2. // On teste si la clé « database.version » est présente dans le conteneur de cache « mon_module »
    3. if ($results = \Drupal::cache('mon_module')->get('database.version')) {
    4. // Si c'est le cas, les données stockées sont dans l'attribut « data »
    5. return $results->data;
    6. }
    7. // Sinon on effectue la requête désirée
    8. $version = $this->connection->select('version', 'v')->fields('v', ['version'])->execute()->fetch();
    9. // Et on met la valeur en cache
    10. \Drupal::cache('mon_module')->set('database.version', $version->version);
    11.  
    12. return $version->version;
    13. }

    Il est possible aussi de donner date d'expiration afin que cette clé de cache ne soit plus valable une fois cette date passée :

    1. // cache valable 1 h (3600 secondes)
    2. \Drupal::cache('mon_module')->set('database.version', $version->version, date('U') + 3600);

     

  • Drupal - Drush - Appeler un script php et lui passer un argument

    Posté le Mardi 14 mai 2019 - 07:24

    Il est possible via drush d’exécuter un script php et de profiter de toute l'API de drupal pour effectuer des traitements (création / suppression de contenu, modification, import de traductions...)

    On utilise pour cela la commande drush php-script en lui passant le chemin vers le script relatif à la racine de drupal :

    1. # Exemple d'appel d'un script
    2. drush @alias php-script ../scripts/process/import-translations.php

    Mais il est aussi possible de passer des arguments à ce script :

    1. #Je passe ici le chemin vers le fichier à importer
    2. drush @alias php-script ../scripts/process/import-translations.php --file=../files/translations/imports/2019-05-14-translations.csv

    Et voici comment le récupérer dans notre script drush :

    1. # Récupération du paramètre file
    2. $file = drush_get_option('file');

    À noter que l'on peut aussi fournir une valeur par défaut :

    1. # ici, si --lang n'est pas passé lors de l'appel du script
    2. # alors $lang prendra la valeur « en »
    3.  
    4. $lang = drush_get_option('lang', 'en');

     

    Tags

  • Drupal 8 - Se connecter à une base de données tierce

    Posté le Lundi 13 mai 2019 - 13:52

    Outre la base de données « classique » de drupal, il est aussi possible de se connecter à une autre base de données.

    Pour cela dans le fichier de settings il faut définir les identifiants :

    1. $databases['seconde_db'] = $databases['default'];
    2. $databases['seconde_db']['default']['host'] = 'HOST_SECONDE_DB';
    3. $databases['seconde_db']['default']['database'] = 'SECONDE_DB';
    4. $databases['seconde_db']['default']['username'] = 'USER_SECONDE_DB';
    5. $databases['seconde_db']['default']['password'] = 'PASSWORD_SECONDE_DB';

    Ensuite dans le code de notre drupal on peut sélectionner cette seconde base de données :

    1. # On sélectionne la base secondaire
    2. Database::setActiveConnection('seconde_db');

    Pour ensuite effectuer les requêtes que l'on souhaite, via la database API de drupal.

    Attention à la fin ne pas oublier de rebasculer sur la base de données par défaut afin de ne pas casser tout le drupal :

    1. # On bascule sur la base de données par défaut
    2. Database::setActiveConnection();

     

  • Optimiser les tâches lourdes de composer avec Drupal

    Posté le Dimanche 12 mai 2019 - 18:37

    Une petite dépendance à ajouter à son composer.json qui permet d'économiser pas mal de ram lors des taches lourdes de composer (update notamment)

    1. composer require zaporylie/composer-drupal-optimizations:^1.1

    Simplement en supprimant des anciens tags des package de symfony, cela peut diviser par deux la mémoire vive nécessaire.

    Plus d'informations : https://github.com/zaporylie/composer-drupal-optimizations

     

  • Gitlab CI - Ne lancer une tâche que lorsque que certains fichiers sont modifiés

    Posté le Samedi 4 mai 2019 - 13:09

    J’utilise énormément la partie « CI » de Gitlab pour déployer automatiquement les sites et applications web que je gère.

    Lors d’une formation sur le sujet, nous avons avec mon stagiaire mis en place une condition à l’exécution d’une tâche (job). Le but était de ne lancer la tache gulp en charge de générer les fichiers CSS et javascript uniquement si un des fichiers sources (sass ou js) était modifié, et non plus à chaque déploiement.

    Voici la définition de la tâche en question dans le fichier .gitlab-ci.yaml

    1. prod_generate_assets:
    2.   image: node:10.15.2
    3.   script:
    4. - npm install -g gulp
    5. - npm install --silent
    6. - gulp build
    7. - ...
    8.   only:
    9.   refs:
    10. - master
    11.   changes:
    12. - web/themes/custom/**/*.scss
    13. - web/themes/custom/**/*.js
    14.   except:
    15.   refs:
    16. - schedules
    17.   stage : postdeploy

    Ainsi avec la directive changes, cette tâche ne sera lancée que si un fichier *.scss ou *.js est modifié dans un des thèmes du dossier custom.

    Un petit bonjour à Adrien, si tu tombes sur ces lignes !

     

  • Drupal 8 - Créer un fichier avec le « résultat » d'un template

    Posté le Samedi 27 avril 2019 - 11:35

    Voici comment écrire un fichier dans drupal 8 :

    1. $sitemaps_path = 'public://sitemaps/';
    2. // création du dossier
    3. if(file_prepare_directory($sitemaps_path, FILE_CREATE_DIRECTORY)) {
    4. // écriture du fichier (s'il existe, on le remplace)
    5. file_save_data($content, $sitemaps_path . 'sitemap.xml', FILE_EXISTS_REPLACE);
    6. }
    7. else {
    8. \Drupal::logger('sitemap')->error(t('Problem creating the folder @folder', ['@folder' => $sitemaps_path]));
    9. }

    Imaginons que l'on veuille écrire dans un fichier le contenu d'un renderable array voici comment l'on définit $content :

    1. $datas = [
    2. '#theme' => 'xml_sitemap',
    3. '#urls' => [
    4. ['title' => 'test'],
    5. ['title' => 'test 2'],
    6. ],
    7. ];
    8.  
    9. // Ici si on ne peut pas utiliser l'injection de dépendance, on pourrait remplacer la ligne suivante par :
    10. // \Drupal::service('renderer')->renderPlain($datas);
    11. $content = $this->renderer->renderPlain($datas);