Message d'avertissement

The subscription service is currently unavailable. Please try again later.

Articles de l'utilisateur

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Créer des éléments de menu dans le code

Note : Il est aussi possible de créer des éléments de menu dans le fichier MODULE.links.menu.yml, cf : https://kgaut.net/snippets/2017/drupal-8-creer-un-element-de-menu-vers-… et https://kgaut.net/snippets/2018/drupal-8-drupal-9-menu-ajouter-un-eleme…

Les extraits de codes suivants peuvent être placer dans des fonctions d'update (MODULE_UPDATE_9001() par exemple).

Créer un élément de menu simple vers un nœud dans le menu principal (main)

$menuItem = MenuLinkContent::create([
  'title' => 'Mon Menu avec un lien vers un noeud',
  'link' => ['uri' => 'internal:/node/5'],
  'menu_name' => 'main',
  'expanded' => TRUE,
  'weight' => 2,
]);
$menuItem->save();

 

Créer un élément de menu sans lien

$menuItem = MenuLinkContent::create([
  'title' => 'Mon menu sans lien',
  'link' => ['uri' => 'route:nolink'],
  'menu_name' => 'main',
  'expanded' => TRUE,
  'weight' => 2,
]);
$menuItem->save();

 

Un élément de sous-menu

$childMenuItem = MenuLinkContent::create([
  'title' => 'Mon sous menu',
  'link' => ['uri' => 'internal:/node/4'],
  'menu_name' => 'main',
  'expanded' => TRUE,
  'parent' => 'menu_link_content:' . $menuItemParent->uuid(),
  'weight' => 2,
]);
$childMenuItem->save();

 

Arborescence complète

Et voici un exemple complet, à adapter à votre besoin pour créer une arborescence, c'est largement perfectible, mais ça fait le taf.

  $items = [
    'Mon élément parent' => [
      'uri' => 'route:<nolink>',
      'children' => [
        'Un menu item' => 'internal:/node/5',
        'Une autre item' => 'internal:/node/6',
      ],
    ],
    'Un autre parent' => [
      'uri' => 'route:<nolink>',
      'children' => [
        'Salut' => 'internal:/node/7',
        "c'est" => 'internal:/node/8',
        'cool' => 'internal:/node/9',
      ],
    ],
  ];
  $poids = 0;
  foreach($items as $parent => $data) {
    $menuItem = MenuLinkContent::create([
      'title' => $parent,
      'link' => ['uri' => $data['uri']],
      'menu_name' => 'main',
      'expanded' => TRUE,
      'weight' => ++$poids,
    ]);
    $menuItem->save();
    foreach ($data['children'] as $child => $subdata) {
      $childMenuItem = MenuLinkContent::create([
        'title' => $child,
        'link' => ['uri' => $subdata['uri']],
        'menu_name' => 'main',
        'expanded' => TRUE,
        'parent' => 'menu_link_content:' . $menuItem->uuid(),
        'weight' => ++$poids,
      ]);
      $childMenuItem->save();
    }
  }

 

Par kgaut
Adhérent
Kevin Gautreau

Vidéo mini formation : Administrer son site drupal avec drush

Petite vidéo que je souhaitais faire depuis longtemps : une présentation de l'utilitaire numéro 1 de tout·e développeur·euse Drupal : Drush !

Drush est un utilitaire en ligne de commande, qui fonctionne sous linux, windows et macos permettant d'exécuter des commandes d'administration sur son site Drupal :

Vidage de cache
Activation de module
Export / Import de configuration (voir mon autre vidéo sur la gestion de la configuration)
Et plein d'autres choses

Depuis quelques temps, drush s'interface avec Drupal Code Generator, et permet de scaffolder des modules, des types d'entités, des formulaires des blocs et bien d'autres chose. Cette fonctionnalité est très pratique et je l'utilise presque tous les jours. C'est ce que permettait de faire drupal console, qui du coup est maintenant beaucoup moins utile.

Dans la vidéo qui suit je vous présente les bases de drush, les commandes de base et les générateurs.

Retrouver le support à l'adresse suivante : https://slides.kgaut.net/presentations/2021/drush.html

Quelques liens utiles :

Le site officiel de Drush : https://www.drush.org
Drush Launcher : https://github.com/drush-ops/drush-launcher
Liste des commandes drush : https://www.drush.org/latest/commands/all/

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Thème Gin - ajouter une feuille de style personnalisée

Gin est mon thème backoffice coup de coeur de ces derniers mois, il donne un coup de fouet au backoffice de drupal !

Gin

 Thème
 Backoffice

Gin est un thème backoffice « moderne » qui intègre un mode dark.

En cours de test de mon côté, pour l'instant je suis convaincu, on verra si ça tiens dans la durée.

Modules additionnels recommandés :

gin_toolbar : Fournir une toolbar optimisée pour le thème
gin_login : Un bel écran de connexion !

Lire la suite de Gin

Il est possible de lui adjoindre une feuille de style css pour surcharger quelques propriétés sans avoir à lui créer un thème enfant, très pratique !

Pour cela rien de plus simple, il suffit de créer un fichier gin-custom.css dans le répertoire public:// donc la plupart du temps : web/sites/default/files/gin-custom.css.

Attention, généralement, ce dossier est exclu du versionning, mais il est possible de forcer l'ajout d'un fichier avec la commande :

git add -f web/sites/default/files/gin-custom.css

Note : cette manipulation est possible depuis la version 8.x-3.0-alpha31, sortie le 02 décembre 2020 (plus d'informations ici : https://www.drupal.org/project/gin/issues/3179676)

Encore mieux, ce thème utilise des variables CSS pour les couleurs, si vous souhaitez changer les couleurs de « focus » c'est possible avec les lignes suivantes, à ajuster en fonction de vos goûts :

* {  --colorGinPrimary: #cc7700;  --colorGinFocus: #cc7700;  --colorGinPrimaryActive: #ff9900;  --colorGinPrimaryHover: #ff9900;}

 

Par kgaut
Adhérent
Kevin Gautreau

Sideproject : Tableau de bord de gestion de mes projets de maintenance

Petit article un peu particulier afin de présenter mon dernier side-project : un outil de gestion de mes projets de maintenance.

Jusqu'à peu, je gérais mes projets de maintenance dans des feuilles de calcul où pour chaque intervention, je notais le temps passé, pour ensuite facturer en fin de mois.

Dans le même temps, je travaille avec de multiples clients qui ont chacun leur système de ticket (github, gitlab.com, gitlab auto-hébergé, pivotal tracker...) et donc je n'avais pas non plus de vue globale sur l'ensemble de mes tickets à traiter au niveau de tous mes clients.

Tombant par hasard sur un module drupal gitlab_api, l'idée m'est venue de faire un outil pour lister l'ensemble de mes tickets gitlab (avec dans l'idée ensuite de faire des connecteurs avec d'autres systèmes). Le module n'accepte par défaut la connexion à un seul serveur, j'ai donc proposé une nouvelle fonctionnalité : l'utilisation de config entities, afin de gérer de multiples connexions (gitlab.com, et autant de gitlab auto-hébergés que nécessaires).

J'ai par la suite développé un module équivalent pour me connecter à l'api de github, puis un autre pour me connecter à pivotal tracker.

Image

Liste des serveurs gitlab

 

Image

Ajout d'un serveur gitlab

 

L'idée ensuite est de pouvoir ajouter des « clients » et de leur affecter un projet.

Voici la liste des clients, avec vision des tickets en cours, du CA global, du CA en attente de facturation...

Image

Listing des clients

 

Chaque projet étant lié à un dépôt gitlab / github / pivotal tracker :

Image

Ajout d'un projet

 

Ainsi, automatiquement sont récupérés les tickets liés à ce projet. Chaque ticket peut-être estimé si le client le demande.

Image

Liste des tickets

 

Ces tickets sont récupérés et mis à jour à intervalle régulier, via une tache cron.

Sur la capture précédente, on voit l'ensemble des tickets (clôturé ou actif) d'un seul projet, mais via les filtres au-dessus du tableau, je peux voir l'ensemble des tickets au niveau global / par client / par projet…

Ensuite, à chaque ticket, je peux affecter une « tâche », correspondant à une intervention effectuée sur le projet et qui devra être facturée.

Image

Ajout d'une tâche

 

Ces tâches sont enfin visibles sur un tableau de bord, lui aussi filtrable par client / projet / ticket / statut de facturation / facture :

Image

listing de tâches

 

En fin de période de facturation, je sélectionne les tâches que je souhaite facturer et renseigne mon numéro de facture, ainsi ces tâches passent en état « facturé ». L'intégration avec ma solution de comptabilité n'est pas encore faite, mais le processus de création est grandement simplifié, je n'ai quasiment plus qu'à saisir le total.

Le client à lui aussi accès à un tableau de bord, où il peut voir toutes ses tâches en cours, et donc les dépenses engagées. Il peut aussi consulter le détail d'une facture :

Image

tableau de bord client

 

Chaque intervention était liée à un ticket, en cliquant sur le lien, il tombera directement sur le ticket correspondant sur gitlab / github / pivotal tracker…

J'ai pu importer tous mon historique de tâches qui étaient dans des google docs en passant par des fichiers csv et un petit script de parsage.

Au niveau technique c'est un petit drupal 9.1.0 (mis à jour hier, jour de la sortie de la première version stable) avec peu de modules activés.

Les clients, projets, tickets et tâches sont des types d'entités personnalisé. les listing sont créés directement via des ListBuilder et non pas des vues. Les tickets se mettent à jours via des QueueWorkers.

Il y a pas mal de champs calculés pour, par exemple avoir au niveau d'un client le CA facturé, le CA en cours... J'ai factorisé mon code au maximum.

Le core de cet applicatif consiste en un seul module « custom » qui nécessite donc 3 autres modules :

gitlab_api : pour s'interconnecter avec plusieurs serveurs gitlab, module tiers disponible sur drupal.org auquel j'ai contribué.
github_api : pour s'interconnecter avec github, module développé par mes soins un peu « quick'n'dirty »
pivotal_api : pour s'interconnecter avec pivotal tracker, développé aussi par mes soins, mais encore trop sale pour être opensourcé pour l'instant.

Dans les faits, le module core est suffisamment générique pour être opensourcé, si vous êtes intéressé, n'hésitez-pas à vous signaler, ça me donnera la motivation à accélérer le processus ! Je pense que d'autres freelances pourraient être intéressés par la solution.

C'est là qu'on voit toute la puissance de drupal (surtout à partir de la version 8) pour développer très rapidement des applicatifs métiers puissants, intégrés et interconnectés. Le tout en codant en se faisant plaisir !

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Surcharger l'autocomplétion d'un champ « entity_reference »

Il est possible dans drupal 8 et 9 de surcharger l'auto-completion d'un champ « référence à un type d'entité », à la fois la requête générée (pour par exemple faire la recherche sur d'autres champs que le titre, mais aussi le label des éléments retournés.

Attention, la seconde étape diffère entre les versions 8 et 9 de drupal. Logiquement la version drupal 9 fonctionne sur les dernières version de drupal 8, je n'ai pas vérifié. Mais l'inverse ne fonctionne en tout cas pas !

Modification du formulaire (drupal 8 et drupal 9)

Pour cela il faut commencer par altérer le formulaire pour modifier un attribut de notre champ autocomplete :

// mon_module.module (ou bien un fichier de définition de formulaire directement // ici, mon champ « entity_reference » est issue// dashboard:issue est une clé qui sera définie à l'étape 2  function mon_module_form_alter(&$form, FormStateInterface $form_state) {  if ($form['id'] === '...') {    $form['issue']['widget'][0]['target_id']['#selection_handler'] = 'dashboard:issue';  }}

Définition du nouveau filtre de sélection  (drupal 9)

mon_module/src/Plugin/EntityReferenceSelection/IssueSelection.php getConfiguration()['target_type'];     // ici c'est une EntityQuery classique    $query = $this->buildEntityQuery($match, $match_operator);    $or = $query->orConditionGroup();     // Je fais une condition sur le titre de mon contenu mais aussi sur le champ « external_id »    $or->condition('title', $match, $match_operator);    $or->condition('external_id', $match, $match_operator);    $query->condition($or);     if ($limit > 0) {      $query->range(0, $limit);    }    $result = $query->execute();     if (empty($result)) {      return [];    }     $options = [];    /** @var \Drupal\dashboard\Entity\Issue[] $entities */    $entities = $this->entityTypeManager->getStorage($target_type)->loadMultiple($result);    foreach ($entities as $entity_id => $entity) {      $project = $entity->getProject();      $bundle = $entity->bundle();       // Ici, pour chaque résultat, définition du label de l'élément qui sera affiché dans la liste déroulante      $options[$bundle][$entity_id] = Html::escape($project->label() . ' #' . $entity->getExternalId(). ' -  ' . $entity->label() );    }     return $options;  } }

Définition du nouveau filtre de sélection  (drupal 8)

mon_module/src/Plugin/EntityReferenceSelection/IssueSelection.php getConfiguration()['target_type'];     // ici c'est une EntityQuery classique    $query = $this->entityManager->getStorage($target_type)->getQuery();    $or = $query->orConditionGroup();     // Je fais une condition sur le titre de mon contenu mais aussi sur le champ « external_id »    $or->condition('title', $match, $match_operator);    $or->condition('external_id', $match, $match_operator);    $query->condition($or);     if ($limit > 0) {      $query->range(0, $limit);    }    $result = $query->execute();     if (empty($result)) {      return [];    }     $options = [];    /** @var \Drupal\dashboard\Entity\Issue[] $entities */    $entities = $this->entityManager->getStorage($target_type)->loadMultiple($result);    foreach ($entities as $entity_id => $entity) {      $project = $entity->getProject();      $bundle = $entity->bundle();       // Ici, pour chaque résultat, définition du label de l'élément qui sera affiché dans la liste déroulante      $options[$bundle][$entity_id] = Html::escape($project->label() . ' #' . $entity->getExternalId(). ' -  ' . $entity->label() );    }     return $options;  } }

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Ajouter une propriété (basefield) à un type d'entité via une fonction update

Voici comment ajouter un basefield slug à un type d'entité client. La définition de cette propriété se trouvant dans la méthode baseFieldDefinitions de notre type d'entité :

function dashboard_update_9005() {  $entity_type_id = 'client'; // nom machine de notre type d'entité  $fields = ['slug']; // champ(s) à créer  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();  \Drupal::entityTypeManager()->clearCachedDefinitions();  $entity_type = $definition_update_manager->getEntityType($entity_type_id);  foreach ($fields as $field) {    $fieldDefinition = $entity_type->getClass()::baseFieldDefinitions($entity_type)[$field];    $definition_update_manager->installFieldStorageDefinition($field, $entity_type_id, $entity_type_id, $fieldDefinition);  }}

En adaptant la seconde ligne de la fonction, il est possible d'ajouter autant de propriétés que l'on souhaite à notre type d'entité.

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base URL

À la différence d'une propriété « lien » (voir : Drupal 8 & Drupal 9 - Entité - Champ de base « link ») un champ de type « uri » ne prendra qu'une colonne dans votre table de base de données : pas de titre, pas d'options (target...) Mais du coup plus économique si on se fiche de ces attributs.

$fields['url'] = BaseFieldDefinition::create('uri')  ->setLabel(t('Issue URL'))  ->setRequired(TRUE)  ->setDisplayConfigurable('view', TRUE)  ->setDisplayConfigurable('form', TRUE);

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base « Référence à une entité » (entity reference)

Je me suis rendu compte que je n'avais jamais documenté comment ajouter une propriété « référence à une entité » :

Pour un type d'entité « client » :

$fields['client'] = BaseFieldDefinition::create('entity_reference')  ->setLabel(t('Client'))  ->setSetting('target_type', 'client')  ->setDisplayConfigurable('form', TRUE)  ->setDisplayConfigurable('view', TRUE);

Pour un nœud :

$fields['contenu'] = BaseFieldDefinition::create('entity_reference')  ->setLabel(t('Contenu'))  ->setSetting('target_type', 'node')  ->setDisplayConfigurable('form', TRUE)  ->setDisplayConfigurable('view', TRUE);

Il est aussi possible de restreindre le/les bundle(s) :

$fields['contenu'] = BaseFieldDefinition::create('entity_reference')  ->setLabel(t('Contenu'))  ->setSetting('target_type', 'node')      -  ->setSetting('handler_settings', [    'target_bundles' => [      'article' => 'article',      'page' => 'page',    ]  ])  ->setDisplayConfigurable('form', TRUE)  ->setDisplayConfigurable('view', TRUE);

 

Par kgaut
Adhérent
Kevin Gautreau

Lancer une inspection PHP Code Sniffer pour un drupal dans un container docker

Phpcs (ou PHP Code Sniffer) est un inspecteur de code permettant de vérifier la validité du code écrits en fonction de standards.

Pour  l'ajouter à notre projet drupal géré via composer :

composer require squizlabs/php_codesniffer # Ou si vous utilisez docker-compose docker-compose exec php composer require squizlabs/php_codesniffer

Drupal a son propre fichier de règles (un fichier xml) utilisable par phpcs, il est compris dans une dépendance drupal spécifique : coder

Pour l'installer :

composer require drupal/coder:^8.3.1 # Ou avec docker-compose docker-compose exec php composer require drupal/coder:^8.3.1

Une fois coder installé, il faut informer phpcs de l'emplacement du fichier de règles (contenu dans coder) :

#  /var/www/html/ est le docroot de mon projet dans mon container dockerdocker-compose exec php vendor/bin/phpcs --config-set installed_paths /var/www/html/vendor/drupal/coder/coder_sniffer

On vérifie que « Drupal » est maintenant bien présent dans les standards de code installés :

docker-compose exec php vendor/bin/phpcs -i                                                                          # Ce qui devrait vous retourner : # The installed coding standards are PEAR, PSR2, Zend, MySource, Squiz, PSR12, PSR1, DrupalPractice and Drupal

Et voila, nous pouvons maintenant lancer des inspections :

# Note : ici je me contente d'inspecter le module "mespronos" situé dans le dossier : ./web/modules/mespronosdocker-compose exec php ./vendor/bin/phpcs --colors --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,css,info,txt,md,yml ./web/modules/mespronos # on pourrait imaginer inspecter tous nos modules custom avec la commande suivante : docker-compose exec php ./vendor/bin/phpcs --colors --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,css,info,txt,md,yml ./web/modules/custom

Si vous utilisez un makefile (plus d'informations) vous pouvez vous créer un « raccourci » :

## phpcs	: Launch phpcs inspections for ./web/modules/customphpcs:	@docker-compose exec php ./vendor/bin/phpcs --colors --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,css,info,txt,md,yml ./web/modules/custom

Ainsi pour lancer vos inspections, vous n'aurez qu'à lancer la commande « make phpcs »

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Webform - avoir un destinataire dynamique en fonction d'une catégorie

Dans le contexte d'un webform, je voulais pouvoir envoyer une notification à un email en particulier en fonction du sujet du message de contact.

Au niveau de mon webform, mon champ sujet est un select dont les options sont issues d'un vocabulaire de taxonomie :

Image

Webform sujet taxonomy

 Dans le vocabulaire en question, j'ai ajouté un champ de type email field_destinataire_message :

Image

Taxo field email

Ainsi pour chaque sujet de contact, l'administrateur pourra saisir un destinataire différent.

Maintenant reste la partie la plus importante, le gestionnaire de notification du webform.

Création d'un plugin EmailWebformHandler :

web/modules/custom/mon_module/src/Plugin/WebformHandler/ContactWebformHandler.php

get('mail');     // Je recupère la valeur du champ « sujet » (un entier : l'id du terme de taxonomy)    $subjectTid = $webform_submission->getRawData()['subject'];    if (($subject = Term::load($subjectTid)) && $destinataire = $subject->get('field_destinataire_message')->value) {      $recipient = $destinataire;    }    // je définis le destinataire    $message['to_mail'] = $recipient;     parent::sendMessage($webform_submission, $message);  } }

Pour terminer, il faut activer cette notification cela se passe au niveau de notre webform dans l'administration : onglet « paramètres », sous-onglet « Emails / Handlers » : Ajouter un gestionnaire :

Image

webform handler

Sélectionner le handler que l'on vient de créer puis cliquez sur le lien « Ajouter un gestionnaire ».

Et il ne restera plus qu'à configurer la notification comme classiquement, sauf qu'évidement cette fois le champ destinataire n'est pas disponible !

webform handler destinataire

Par kgaut
Adhérent
Kevin Gautreau

Vidéo mini formation : Versionner son projet Drupal avec Git

Une nouvelle vidéo fraîchement mise en ligne pour le weekend. Au programme du jour : Git et comment l'utiliser sur un projet drupal.

Mini introduction sur git et ses fondamentaux
Passage en revue des dossiers et fichiers composants l'architecture d'un projet Drupal
Exemple d'organisation pour travailler en équipe avec les branches Git et les environnements.

Attention, je le répète, dans la troisième parti je présente une organisation de fonctionnement qui me convient à moi et qui répond aux problématiques que j'ai rencontré. Mais ça n'est pas la seule et unique manière de faire et encore moins la meilleure.

Désolé pour le petit faux-contact au niveau du micro qui entraîne parfois un peu de bruit, mon ingé son est viré, le problème est résolu.

Le dépôt utilisé dans la vidéo est librement accessible : https://gitlab.kgaut.net/kgaut/formation-drupal-git

Retrouvez la vidéo sur youtube directement (pensez à passer en plein écran) ou bien juste ci-dessous.

N'oubliez-pas que vous pouvez voter ou me proposer des sujets à l'adresse suivante : https://kgaut.net/suggestion-sujet-video-formation.

Commandes GIT vues dans la vidéo

Initialiser un dépôt

git init

Versionner un fichier et le commiter

git add readme.mdgit commit -m "Ajout fichier readme"

Ajouter une remote

git remote add origin URL_DEPOT_GIT

Créer un tag git

git tag NOMTAG # exemple :  git tag 1.0.0 # ou  git tag 20201030-01

Créer une nouvelle branche git

git checkout -b feature-blog

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Installer un nouveau type d'entité via un HOOK_update

Voici comment installer un type d'entité personnalisé via un hook_update.

À noter, les types d'entités d'un module sont automatiquement installés lors de l'installation du module. Ce snippet n'est utile que pour un type d'entité créé à postériori.

/** * Create entity Type « inscription_newsletter » */function MON_MODULE_update_8008() {  \Drupal::entityTypeManager()->clearCachedDefinitions();  \Drupal::entityDefinitionUpdateManager()->installEntityType(\Drupal::entityTypeManager()->getDefinition('inscription_newsletter'));}

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base « email »

Voici comment définir une propriété (basefield) « e-mail » sur un type d'entité.

    $fields['email'] = BaseFieldDefinition::create('email')      ->setLabel(t('Email'))      ->setDefaultValue('')      ->setDisplayConfigurable('form', TRUE)      ->setDisplayConfigurable('view', TRUE);

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Afficher un formulaire dans un bloc ou un contrôleur

Il est possible dans un contrôleur ou un bloc de récupérer un formulaire et de l'afficher comme n'importe quelle autre variable.

À l'époque de drupal 7 on utilisait la fonction drupal_get_form(), à partir de drupal 8, il faut utiliser le service form_builder et sa méthode getForm() en lui passant la classe du formulaire :

#dans la méthode build de mon bloc ou mon controleur :$build['#mon_formulaire'] = \Drupal::service('form_builder')->getForm(\Drupal\mon_module\Form\LoginForm::class);$build['#theme'] = 'mon_template';

Note : il est toujours préférable d'injecter le service en utilisant l'injection de dépendance.

Ensuite il sera possible d'afficher le formulaire dans le template via la variable mon_formulaire :

{# Dans le template twig : mon-template.html.twig #}{{ mon_formulaire }}

Évidement, il ne faut pas oublier d'avoir déclaré la variable mon_formulaire dans la déclaration du template :

//mon_module.module  function mon_module_theme() {  $themes = [];  $themes['mon_template'] = [    'render element' => 'elements',    'variables' => [      'mon_formulaire' => [],    ],    'template' => 'mon-template',  ];   return $themes;}

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base « Référence à un media »

 Voila comment ajouter une propriétée faisant référence à un média sur un type d'entité :

$fields['header_image'] = BaseFieldDefinition::create('entity_reference')  ->setLabel(t('Header image'))  ->setSetting('target_type', 'media')  ->setSetting('handler_settings', ['target_bundles' => ['image' => 'image']])  ->setDisplayConfigurable('form', TRUE)  ->setDisplayConfigurable('view', TRUE);

Via la ligne ci-dessous, il est possible de spécifier les types de media acceptés

  ->setSetting('handler_settings', ['target_bundles' => ['image' => 'image']])

 

Par kgaut
Adhérent
Kevin Gautreau

Vidéo mini formation : La configuration dans Drupal et le module config_split

Deuxième épisode de mes vidéos de mini-formations à Drupal avec au sujet du jour un point important : la gestion de la configuration dans Drupal.

Qu'est-ce que la configuration ? Comment l'exporter, l'importer, mais aussi et surtout comment, à l'aide du module config_split.

Retrouvez la vidéo sur youtube à l'adresse suivante : https://youtu.be/qy_nq3_sbyM

ou bien directement ci-dessous :

 

Pour proposer des sujets, c'est par ici.

Quelques indications / précisions.

Drush

Installé dans vendor/bin/drush, il peut être appelé directement via se chemin

# Depuis la racine de votre projetvendor/bin/drush COMMANDE

Il est aussi possible d'installer drush-launcher pour éxecuter directement la commande drush : https://github.com/drush-ops/drush-launcher

Activer manuellement un environnement de config split

Dans votre fichier settings.local.php :

// « dev » correspond au nom machine de l'environnement à activer$config['config_split.config_split.dev']['status'] = TRUE;

Les spécificités de l'environnement « prod »

La configuration spécifique de production est exportée dans un dossier ../config/prod

Via un .gitignore, j'exclue du versioning le contenu de ce dossier, afin que si la configuration est modifiée en prod, son export ne gène pas GIT, qu'il ne détecte pas de fichiers modifié.

Process d'un déploiement en production

Qu'il soit automatisé ou non, pour rappel, cette solution me convient personnellement, mais elle ne répond pas forcément à toutes les problématiques, n'hésitez-pas à l'adapter si besoin.

Connexion en SSH au serveur
Script vérifiant qu'aucun fichier n'a été modifié via un git status, si des fichiers sont modifiés alors un mail m'est envoyé et le déploiement est annulé
Script vérifiant que la configuration active n'a pas été modifiée directement en prod. Pour cela un config export est fait, si des modifications sont détectées (encore via un git status) alors un mail m'est envoyé et le déploiement est annulé. La configuration spécifique à l'environnement étant exportée dans un dossier exclu du versioning, alors aucune modification n'est détectée.
git pull (ou )
composer install --no-dev pour insta
drush updb (pour lancer les éventuelles mise à jour de la base de données)
drush config:import afin d'importer la configuration éventuellement modifiée, (la config spécifique de prod ayant été exportée à l'étape 3, elle sera réimportée à ce moment là.

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Views - Utiliser un view_mode spécifique pour un seul élément

Dans la continuité de Drupal 8 & Drupal 9 - Views - créer un Pager personnalisé.

Sur un de mes projets j'ai une vue qui liste des nœuds de type « Article » avec le mode d'affichage (view_mode) « Teaser », mais j'avais besoin d'afficher le premier élément de la première avec un autre mode d'affichage, le contenu prendra toute la largeur, au lieu d'un tiers pour les suivants.

J'ai pour cela utilisé le hook HOOK_views_post_render :

function MON_MODULE_views_post_render(ViewExecutable $view, &$output, \Drupal\views\Plugin\views\cache\CachePluginBase $cache) {  if ($view->id() === 'front_actualites' && $view->current_display === "page" && \Drupal::request()->query->get('page', 0) === 0) {    $output['#rows'][0]['#rows'][0]['#view_mode'] = 'teaser_pinned';  }}

avec

$view->id() === 'front_actualites' && $view->current_display === "page"

Je teste si je suis bien sur la bonne vue avec le bon mode d'affichage

\Drupal::request()->query->get('page', 0) === 0

Me permet de tester le paramètre GET ?page= (qui gère la pagination) en fournissant la valeur par défaut 0, afin de tester que je sois bien sur la première page.

Enfin je change le view_mode de la première ligne de mes résultats :

$output['#rows'][0]['#rows'][0]['#view_mode'] = 'teaser_pinned';

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Views - créer un Pager personnalisé

Pour un projet j'avais besoin d'afficher une pagination particulière sur une vue.

La première page devait afficher 9 éléments, et les suivantes 12.

J'ai donc pour cela créé un Pager personnalisé pour Views, et ajouté ce paramètre en option.

En voici le code, disponible aussi sur github : https://github.com/kgaut/kgaut_tools/blob/2.0.x/src/Plugin/views/pager/…

Ce code est à placer dans un module, au chemin suivant : modules/custom/mon_module/src/Plugin/views/pager/PagerFullWithSpecificFirstPage.php

 10];    unset($options['expose']);    return $options;  }   /**   * {@inheritdoc}   */  public function buildOptionsForm(&$form, FormStateInterface $form_state) {    parent::buildOptionsForm($form, $form_state);    $pager_text = $this->displayHandler->getPagerText();    $form['items_per_page']['#weight'] = -2;    $form['items_per_page_first_page'] = [      '#title' => $pager_text['items per page title'] . ' for the first page',      '#type' => 'number',      '#min' => 0,      '#weight' => -1,      '#description' => $pager_text['items per page description'],      '#default_value' => $this->options['items_per_page_first_page'],    ];     unset($form['expose']);  }   /**   * {@inheritdoc}   */  public function validateOptionsForm(&$form, FormStateInterface $form_state) {    // Only accept integer values.    $error = FALSE;  }   /**   * {@inheritdoc}   */  public function summaryTitle() {    if (!empty($this->options['offset'])) {      if ($this->options['items_per_page'] !== $this->options['items_per_page_first_page']) {        return $this->formatPlural($this->options['items_per_page'], '@count item (@count_first for the first page), skip @skip', 'Paged, @count items, skip @skip', [          '@count' => $this->options['items_per_page'],          '@count_first' => $this->options['items_per_page_first_page'],          '@skip' => $this->options['offset'],        ]);      }      return $this->formatPlural($this->options['items_per_page'], '@count item, skip @skip', 'Paged, @count items, skip @skip', [        '@count' => $this->options['items_per_page'],        '@skip' => $this->options['offset'],      ]);    }    if ($this->options['items_per_page'] !== $this->options['items_per_page_first_page']) {      return $this->formatPlural($this->options['items_per_page'], '@count item', 'Paged, @count items (@count_first for the first page)', [        '@count' => $this->options['items_per_page'],        '@count_first' => $this->options['items_per_page_first_page'],      ]);    }    return $this->formatPlural($this->options['items_per_page'], '@count item', 'Paged, @count items', ['@count' => $this->options['items_per_page']]);  }   /**   * {@inheritdoc}   */  public function query() {    if ($this->current_page === 0) {      $this->options['items_per_page'] = $this->options['items_per_page_first_page'];    }    $limit = $this->options['items_per_page'];    $offset = $this->options['offset'];    if ($this->current_page > 0) {      $offset += ($this->current_page - 1) * $this->options['items_per_page'];      $offset += $this->options['items_per_page_first_page'];    }    if (!empty($this->options['total_pages'])) {      if ($this->current_page >= $this->options['total_pages']) {        $limit = $this->options['items_per_page'];        $offset = $this->options['total_pages'] * $this->options['items_per_page'];      }    }    $this->view->query->setLimit($limit);    $this->view->query->setOffset($offset);  }   public function updatePageInfo() {    if (!empty($this->options['total_pages'])) {      if (($this->options['total_pages'] * $this->options['items_per_page']) total_items) {        $this->total_items = $this->options['total_pages'] * $this->options['items_per_page'];      }    }     // Don't set pager settings for items per page = 0.    $items_per_page = $this->getItemsPerPage();    $items_per_page_first = $this->getItemsPerPageFirst();    if (!empty($items_per_page)) {      // quick fix if specific number on first page      $total_items = (int) $this->getCurrentPage() !== 0 ? $this->getTotalItems() + $items_per_page_first - 1 : $this->getTotalItems();      $pager = $this->pagerManager->createPager($total_items, $this->options['items_per_page'], $this->options['id']);      // See if the requested page was within range:      if ($this->getCurrentPage() >= $pager->getTotalPages()) {        $this->setCurrentPage($pager->getTotalPages() - 1);      }    }  }   public function getItemsPerPageFirst() {    return isset($this->options['items_per_page_first_page']) ? $this->options['items_per_page_first_page'] : 0;  } }

Vous pouvez retrouver

Par kgaut
Adhérent
Kevin Gautreau

Composer - Drupal - résoudre l'erreur "undefined index: extra"

Depuis quelques jours, on peut rencontrer l'erreur «  undefined index: extra » lors d'un composer update ou composer require.

Si vous rencontrez ce soucis, exécutez la commande :

composer update zaporylie/composer-drupal-optimizations --no-plugins

Cela devrait résoudre le soucis.

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

Par kgaut
Adhérent
Kevin Gautreau

Vidéo mini formation : Installer drupal 9 avec composer

J'ai mis en ligne tard vendredi soir ma première vidéo de mini-formation à drupal.

J'ai commencé par un sujet basique : Le téléchargement et l'installation du CMS Drupal avec composer.

La vidéo est disponible gratuitement sur youtube et dure 20 minutes.

Je ne m'étais jamais confronté avant au format vidéo, et même si j'ai déjà donné des conférence et animé des formations, le format est évidement complètement différent.

Le résultat n'est pas parfait, mais j'en suis assez content. N'hésitez-pas à me faire vos retours sur le fond comme sur la forme.

Si vous souhaitez me suggérer un sujet pour une prochaine vidéo, vous pouvez le faire à la page suivante : https://kgaut.net/suggestion-sujet-video-formation.

Pour info, je suis déjà en train de préparer le conducteur de la prochaine qui présentera la gestion de la configuration et le module config split dans Drupal.

On m'a posé la question du temps passé et de mon organisation, voici en détail les étapes.

J'ai commencé évidement à définir le conducteur et les différents liens à afficher en me servent de notion.so, environ 2h de préparation.

Image

Conducteur drupal 9 avec composer - notion

 

J'ai ensuite fait l'enregistrement avec le logiel OBS, très bon soft, super facile à prendre en main. Il permet de mixer les "scènes" (captation d'un écran, d'une webcam, image fixe) et les sources de son (micro, pc...). La captation m'a pris environ 1h30. J'avais divisé la captation en séquences, une pour chaque chapitre, me permettant de reprendre mon souffle et de boire un peu d'eau entre chaque. Pour chaque séquence, il m'a souvent fallu plus de 5 prises pour en avoir enfin une correcte.

Enfin le montage a été fait avec le logiciel Kdenlive. Ici aussi très facile à prendre en main. Cette phase à consisté à enchaîner les séquences filmées et en faisant des "cuts", quand c'était nécessaire (bafouille, répétition...) Pour cette partie en comptant le visionnage final : 2H00, sans compter le temps de rendu, environ 30 minutes.

Soit un total d'environ 6H pour 20 minutes utiles de vidéo.

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Ajouter un champ Media dans un formulaire et l'afficher dans un template

Voici comment ajouter un champ de type « référence à un media » dans un formulaire personnalisé en utilisant le widget Media Library :

    $form['video'] = [      '#type' => 'media_library',      '#allowed_bundles' => ['video', 'remote_video'],      '#title' => t('Video'),      '#default_value' => $config->get('video') ?? NULL,    ];

la clé #allowed_bundles contient la liste des types de media que l'on veut autoriser

Voici ensuite comment le rendre dans un template via un controller :

Dans le controller :

if($config['video'] && $media = Media::load($config['video'])) {    $config['video'] = $this->entityTypeManager->getViewBuilder('media')->view($media, 'default');}     return [   '#theme' => 'mon_template',   '#data' => $config,];

et dans le template twig, un simple :

{{ data.video }}

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & 9 - Menu - définir comme parent un élément de menu créé via l'UI

Dans le fichier mon_module.links.menu.yml, on peut facilement placer un élément de menu dans une arborescence via la clé parent.

mon_module.admin_mon_module_config_form:  title: 'Configuration Mon Module'  route_name: mon_module.admin_resalys_config_form  description: 'Configuration webservice de mon module'  parent: system.admin_config_services  weight: 10

Ici le parent fait référence à la clé d'un autre élément de menu.

Mais parfois le parent voulu a été créé via l'interface, et donc le parent doit être référencé via son UUID :

mon_module.admin.structure.settings:  title: 'Partner settings'  description: 'Configure Partner entities'  route_name: partner.settings  parent: 'menu_link_content:4bbda5ed-eeee-4eab-be01-e3d7349f4daa'

L'UUID est trouvable dans la base de données, ou bien en passant par l'onglet devel de l'élément de menu « parent » voulu :

Image

Drupal menu item uuid

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Utiliser des vues différentes selon les taxonomies

À partir de drupal 8, c'est une vue qui gère la page des contenus d'un terme de taxonomie.

Le soucis est que c'est la même vue pour l'ensemble des vocabulaires.

Pour le site courant je voulais une page différente pour les catégories de module (exemple avec la catégorie backoffice) de la page par défaut (exemple avec le tag drupal).

Pour cela il faut surcharger le contrôleur gérant les vues, et n'ayez pas peur c'est moins compliqué à faire qu'à dire.

Déclaration du service

Le service va altérer la route /taxonomy/term/% pour changer le contrôleur qui sera executé

modules/custom/mon_module/mon_module.services.yaml

services:  mon_module.route_subscriber:    class: Drupal\mon_module\Routing\TaxonomyListingViewsRoutingSubscriber    tags:      - { name: event_subscriber }

Définition du service

modules/custom/mon_module/src/Routing/TaxonomyListingViewsRoutingSubscriber.php

get('entity.taxonomy_term.canonical')) {      $route->setDefault('_controller', '\Drupal\mon_module\Controller\TaxonomyTermViewPageController::handle');    }  }   public static function getSubscribedEvents() {    $events = parent::getSubscribedEvents();    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -180);    return $events;  } }

Surcharge du contrôleur

modules/custom/mon_module/src/Controller/TaxonomyTermViewPageController.php

getParameter('taxonomy_term');     $vid = $term->get('vid')->first()->getValue();    $vid = $vid['target_id'];     //on teste le vocabulaire    if ($vid === 'modules_categories') {      $view_id = 'front_modules';      $display_id = 'page_categories';    }     return parent::handle($view_id, $display_id, $route_match);  } }

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal - Améliorer l'expérience d'administration

Note : Quand on débute avec drupal, le plus dur est de connaître l'ensemble de l'écosystème. Entre-autres, l'ensemble des modules tiers (contrib) qui existent. J'inaugure une nouvelle section sur ce site avec la présentation (rapide) de différents modules que je conseille. Cela se passe sur la page Modules Drupal. Je vais tenter aussi de temps en temps de mettre en avant des modules par thématique. c'est le cas ici.

Le backoffice de drupal est réputé pour être austère, même si cela s'est amélioré depuis les dernières version, ça n'est pas complètement faux !

Mais via quelques modules et thème il est possible d'améliorer grandement cette expérience.

Vous trouverez ci-dessous quelques modules que je conseille dans cet objectifs. N'hésitez-pas à ajouter les votre.

Adminimal

 Backoffice
 Thème

Le thème d'administration que j'utilise sur la plupart de mes sites. En en faisant un thème enfant évidement.

Lire la suite de Adminimal

 

Admin Toolbar

 Backoffice
 Indispensable

Admin Toolbar transforme la barre d'administration native de drupal (module toolbar) en menu déroulant. Oui ce module est indispensable.

À noter, ce module contient différents sous-modules :

Lire la suite de Admin Toolbar

 

Coffee

 Backoffice
 Indispensable

Le module Coffee permet aux administrateur de déclencher via un raccourci clavier (alt+k ou alt+d) un petit moteur de recherche qui indexe tous les éléments du menu d'administration.

Ainsi, plus besoin de chercher pendant des heures où est cachée la page de configuration de cet obscure module.

Lire la suite de Coffee

 

Field Group

 Backoffice

Permet de regrouper des champs dans des onglets sur les écrans de création de contenu.

Il est ainsi possible d'avoir des formulaires mieux « rangés »

Lire la suite de Field Group

 

Module filter

 Backoffice

Quand on commence à avoir une liste de module importante, la page par défaut n'est pas très utilisable.

Ce module permet de regrouper les modules par package dans des onglets et ajoute un champ de recherche javascript.

Lire la suite de Module filter

 

Environment Indicator

 Backoffice

Ce module tout simple permet de modifier la couleur de la barre d'administration en fonction d'un environnement (prod / preprod / staging / dev...)

La sélection de l’environnement courant peut se faire de plusieurs façon, le plus simple à mon sens est via le fichier settings.local.php :

Lire la suite de Environment Indicator

 

Node Edit Protection

 Backoffice
 Rédaction contenu
 Indispensable

Il vous est déjà arrivé de travailler sur un contenu pendant un certain temps sur votre site, et de quitter la fenêtre par erreur ou bien de faire précédent, et ainsi de perdre tout ce que vous aviez fait ? Moi oui...

Ce module ajoute une demande de confirmation javascript dans ces cas là ❤️

Lire la suite de Node Edit Protection

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 et Drupal 9 - Créer une archive Zip

Voici comment créer une archive zip et y ajouter des fichiers.

Pour commencer, nous aurons besoin d'injecter deux services, (ici je suis dans un formulaire, à adapter en fonction du contexte)

  public static function create(ContainerInterface $container) {    $instance = parent::create($container);    $instance->fileSystem = $container->get('file_system');    $instance->archiver = $container->get('plugin.manager.archiver');    return $instance;  }

Création de l'archive en tant que telle :

// $files contient un tableau de chemin de fichiers à ajouter à l'archive$files = ['private://monfichier1.pdf', 'private://monfichier2.pdf']; // Chemin où sera enregistré l'archive$archivePath = 'private://pdf-export/mon-archive.zip'; // On crée l'archive physiquement sur le disque, avec un contenu vide// FileSystemInterface::EXISTS_REPLACE indique que si le fichier existe déjà on le remplace, il existe d'autres options$this->fileSystem->saveData('', $archivePath, FileSystemInterface::EXISTS_REPLACE); //On récupère l'objet Zip pointant vers l'archive que nous venons de créer./** @var Zip $zip */$zip = $this->archiver->getInstance(['filepath' => $archivePath]); foreach ($files as $file)  {  $filepath = $this->fileSystem->realpath($file);  $zip->add($filepath, $filename);}

Et voila, cela fonctionne mais ça n'est pas forcément top... Pourquoi pas forcément top ? car toute l’arborescence des différents fichiers est recrée dans l'archive :

Image

Archive Zip

 Ça n'est pas forcement très gênant, mais dans mon cas je préférais avoir tous les fichiers à la racine de mon archive. Voila comment faire :

// $files contient un tableau de chemin de fichiers à ajouter à l'archive$files = ['private://monfichier1.pdf', 'private://monfichier2.pdf']; // Chemin où sera enregistré l'archive$archivePath = 'private://pdf-export/mon-archive.zip'; // On crée l'archive physiquement sur le disque, avec un contenu vide// FileSystemInterface::EXISTS_REPLACE indique que si le fichier existe déjà on le remplace, il existe d'autres options$this->fileSystem->saveData('', $archivePath, FileSystemInterface::EXISTS_REPLACE); //On récupère l'objet Zip pointant vers l'archive que nous venons de créer./** @var Zip $zip */$zip = $this->archiver->getInstance(['filepath' => $archivePath]); // $zip est juste un wrapper Drupal vers un objet \ZipArchive que l'on va récuprérer ici dans $zipArchive$zipArchive = $zip->getArchive(); foreach ($files as $file)  {  // on récupère le nom de fichier, sans le chemin  $filename = basename($file);  $filepath = $this->fileSystem->realpath($file);   // on ajoute le fichier à l'archive mais en indiquant comme chemin uniquement le nom du fichier, ainsi, plus de création de dossier  $zipArchive->addFile($filepath, $filename);}

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 et Drupal 9 - EntityQuery - faire une condition sur un champ d'une entité liée

Petite découverte datant d'il y a quelques mois, il est possible via une EntityQuery d'ajouter une condition sur un champ d'une entité liée.

Petit exemple :

J'ai un type d'entité game (match), lié à un type d'entité day (journée), lui même lié à un type d'entité league (compétition)

(Match : OM - ASSE, journée : Journée 1, Compétition : Ligue 1 2020-2021)

Image

MPD MesPronos

 

Si je souhaite récupérer tous les matchs d'une compétition donnée (mettons la 12,) je peux me baser sur l'attribut days.league : 

$query = \Drupal::entityQuery('game');$query->condition('day.entity:day.league', 12);$ids = $query->execute();

 

Mais on peut aussi aller plus loin et remonter encore d'un niveau pour faire une condition sur un attribut de la compétition de la journée des matchs. Pour ainsi récupérer l'ensemble des matchs des compétitions active (attribut leagues.status) :

$query = \Drupal::entityQuery('game');$query->condition('day.entity:day.league.entity:league.status', 'active');$ids = $query->execute();

Source : https://www.drupaleasy.com/quicktips/drupal-8-entity-query-across-throu…, partagé sur le slack de l'association Drupal France

Par kgaut
Adhérent
Kevin Gautreau

Les forks et merge-requests arrivent (enfin) sur drupal.org !

Depuis la migration des dépôts du code de Drupal ainsi que de l'ensemble des modules tiers sur Gitlab, nous sommes nombreux à attendre que l'ensemble des possibilités offertes par ce changement soient accessibles.

Une d'entre elle qui simplifiera grandement la proposition de modification de code par les néophytes est la possibilité de faire des « merge requests », à l'image de ce qui se fait sur une instance gitlab ou bien des pull-requests sur github.com.

Actuellement pour proposer une modification sur le core de Drupal ou l'un de ses modules tiers, il faut cloner le dépôt sur sa machine, faire les modifications, générer un patch et enfin proposer le patch. Procédure qui n'est pas inaccessible, mais qui n'est pas simple pour les nouveaux venus. Surtout quand on veut corriger juste un petit truc.

Le nouveau fonctionnement sera beaucoup plus simple. Il faudra commencer par créer un issue sur le projet que l'on souhaite amender :

Image

Drupal create issue

 

Note : retrouvez le ticket ici : https://www.drupal.org/project/apidae_tourisme/issues/3160451

Une fois cette issue créé, l'issue tracker de drupal proposera de créer un fork (copie) du dépôt. Fork qui sera dédié uniquement au traitement de ce ticket. Si deux personnes veulent proposer deux approches pour un même problème, alors deux forks peuvent être créés.

Image

Drupal issue fork

 

L'adresse du fork en question : https://git.drupalcode.org/issue/apidae_tourisme-3160451

Une fois créé, le fork sera clonable et/ou éditable en ligne directement via le web IDE de Gitlab :

Image

Gitlab web ide

 

Au commit, l'IDE nous proposera de directement créer une merge request, fonctionnalité qui est aussi possible depuis l'interface de Gitlab si l'on fait nos modifications en local et pas dans le web IDE.

Image

gitlab drupal merge request

 

Voici l'écran de création d'une merge request :

Image

Drupal merge request creation

 

Une fois créée, la MR est visible dans le ticket :

Image

Drupal issue merge request

 

Image

Drupal issue merge request 2

 

Ensuite l'écran d'une merge request permet de voir les modifications, de faire des commentaires, et d'accepter ou non la modification (voir en ligne : https://git.drupalcode.org/project/apidae_tourisme/-/merge_requests/1)

Image

Drupal merge request changes

 

Image

Drupal merge request screen

 

Ainsi on peut se passer complètement de la procédure de patch, même pour des grosses modifications ! Je trouve personnellement la procédure plus simple mais surtout plus accessible pour un·e néophyte.

Actuellement la fonctionnalité est encore en beta, il faut demander l'activation pour un ou plusieurs de ses projets personnels sur ce ticket : https://www.drupal.org/project/drupalorg/issues/3152637.

Si vous voulez jouer un peu et tester la procédure, n'hésitez pas à le faire sur le dépôt pour lequel j'ai activé la fonctionnalité, je ferai le ménage plus tard : https://www.drupal.org/project/apidae_tourisme/issues/3160451 (vous prouvez créer de nouveaux tickets si besoin en mettant « test » dans le titre.

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 et Drupal 9 - Créer une commande drush personnalisée

Voici un exemple rapide de commande drush appelant une méthode d'un service avec une option lors de l'appel.

À noter : Drupal\mon_module\Service\Resalys est un service existant, je souhaite utiliser la méthode syncResalys de ce service qui accepte un paramètre optionnel : $force

Déclaration de la commande

modules/mon_module/drush.services.yml

services:  mon_module.drush.resalys: # clé du fichier de commande, à définir vous même    class: Drupal\mon_module\Command\ResalysCommand # Namespace de la classe contenant la commande    arguments:      - '@mon_module.resalys' # Service à injecter dans notre commande    tags:      -  { name: drush.command }

Définition de la commande

modules/mon_module/src/Command/ResalysCommand.php

resalys = $resalys;  }   /**   * Sync resalys cache   *   * @command resalys:sync   * @usage drush resalys:sync   * @usage drush resalys:sync --force   *   * @param int[] $options   */  public function sync($options = ['force' => FALSE]) {    $force = (bool) $options['force'];    $this->resalys->syncResalys($force);  } }

Utilisation

drush resalys:syncdrush resalys:sync --force

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 et Drupal 9 - Attacher une librairie CSS ou JS à une vue

Voici comment inclure des fichiers CSS ou JS sur une page contenant une vue.

Pour rappel les librairies peuvent être définies dans un thème ou un module.

Utilisation du hook HOOK_views_pre_render.

Dans le .module de votre module :

function MON_MODULE_views_pre_render(\Drupal\views\ViewExecutable $view) {  if ($view->storage->id() === 'front_blog') {    $view->element['#attached']['library'][] = 'theme/swiper-library';    $view->element['#attached']['library'][] = 'theme/univers-js';  }}

 

Par kgaut
Adhérent
Kevin Gautreau

Drupal 8 & Drupal 9 - Entity Query - Ajouter une condition sur une colonne spécifique

Dans le cadre d'une EntityQuery, il peut être nécessaire parfois de faire une requête sur une colonne spécifique de notre table, autre que le traditionnel « value ».

Rien de bien compliqué, il faut alors le spécifier dans le nom du champ sur lequel on ajoute une condition.

Exemple normal :

// Condition sur la colonne « value » du champ « field type »$query->condition('field_type', 'FETE_ET_MANIFESTATION');

Pour spécifier la colonne, on concatène le nom de la colonne avec le nom du champ avec un point :

// Condition sur la colonne « dateDebut » du champ « field_apidae_dates »$query->condition('field_apidae_dates.dateDebut', $now->format('Y-m-d'), '>');

 

Pages