[Drupal] Envoyer un email lorsque le champ d'un contenu a été mis à jour

Graffiti d'un bonhomme rouge qui fait coucou
Création d'un module Drupal qui envoie une notification par mail lorsque le champ d'un node a été modifié.

Un besoin simple, une solution simpliste

Le contexte : lorsque les horaires des établissements de l'entreprise ont été modifiées sur le site Drupal (par un utilisateur ou par l'importation de données via un webservice), nous devons alerter les personnes en charge de mettre à jour les horaires manuellement sur la célèbre plateforme qui référence tout ce qui existe dans l'univers.

Dans notre cas, il y a une cinquantaine d'établissements, donc 1 ou 2 modifications d'horaires au total par mois... inutile de complexifier notre développement avec la Queue API. Il faudra cependant l'envisager suivant vos besoins, car si les modifications sont trop nombreuses, l'envoi massif de mails risque de saturer le système... il faudra donc préférer l'utilisation de files d'attente (queue) qui déclencheront l'envoi des mails par lots lors de l'exécution du cron.

Notre solution consiste en l'implémentation de 2 fonctions, des hooks, qui seront à placer dans le fichier .module de notre module "alert_on_change".

 

Déclenchement de l'alerte

Pour envoyer notre alerte mail à chaque modification du champ, il faut implémenter le hook_entity_presave() qui se déclenche à chaque fois qu'une entité est modifiée.

/**
 * Implements hook_entity_presave()
 */
function alert_on_change_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity instanceof Drupal\node\NodeInterface && $entity->bundle() == 'agency') {
    if ($entity->field_opening_time->value !== $entity->original->field_opening_time->value) {
      // TODO: opening time has been changed,
      // we should collect some infos and then send an email
      ...
    }
  }
}

Dans ce code, le premier test vérifie qu'il s'agit d'un nœud de type agence, le second que la valeur du champ horaire a changé.

 

Envoi de l'email

Il s'agit maintenant de récupérer quelques infos à inclure dans le mail comme les anciens et nouveaux horaires. Ces infos sont transmises à la fonction hook_mail() grâce au dernier paramètre sous la forme d'un tableau ($params).

/**
 * Implements hook_entity_presave()
 */
function alert_on_change_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity instanceof Drupal\node\NodeInterface && $entity->bundle() == 'agency') {
    if ($entity->field_opening_time->value !== $entity->original->field_opening_time->value) {
      $params['node'] = $entity;
      $params['previous_value'] = $entity->original->field_opening_time->value;
      $params['new_value'] = $entity->field_opening_time->value;
      /** @var \Drupal\Core\Mail\MailManager $mailManager */
      $mailManager = \Drupal::service('plugin.manager.mail');
      $mailManager->mail(
        'alert_on_change',
        'opening_time_changed',
        'team-gmb@example.fr, digital-marketing@example.fr',
        \Drupal::languageManager()->getCurrentLanguage()->getId(),
        $params
      );
    }
  }
}

Il faut ensuite implémenter hook_mail() pour personnaliser le contenu du mail et notamment inclure les infos qui ont été transmises via le dernier paramètre...

/**
 * Implements hook_mail().
 */
function alert_on_change_mail($key, &$message, $params) {
  switch ($key) {
    case 'opening_time_changed':
      $node = $params['node'];
      $message['subject'] = t('Changement d\'horaires - @node_title', [
        '@node_title' => $node->getTitle(),
      ]);
      $body = t('
Bonjour 🙂,

Les horaires de "[node:title]" ont été modifiés

🙅‍♀️ Anciens horaires :
@previous_value

💁‍♀️ Nouveaux horaires :
@new_value

-- [site:name]
[site:url]
      ', [
        '@previous_value' => $params['previous_value'],
        '@new_value' => $params['new_value']
      ]);
      $message['body'] = [
        '#markup' => \Drupal::token()->replace($body, [
          'node' => $node
        ]),
      ];
      break;

    default:
      $message['subject'] = $params['subject'];
      $message['body'][] = $params['body'];
      break;
  }
}

Vous noterez que le corps du mail utilise 2 types de variables :

  • les variables préfixées par @ qui sont remplacées par la fonction de traduction t()
  • les tokens entre crochets qui sont remplacés par la fonction \Drupal::token()->replace()

 

Une solution plus moderne ?

Récemment j'ai découvert dans cet article que nous pouvons utiliser des "bundle class" pour implémenter des fonctionnalités spécifiques à un bundle... On peut donc imaginer l'implémentation de la méthode AgencyNode::postSave() qui reprendrait le code de notre hook_entity_presave()... quand au hook_mail() je ne crois pas qu'on puisse s'en passer... à creuser...

Mots clés

Ajouter un commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.

Texte brut

  • Aucune balise HTML autorisée.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Les adresses de pages web et les adresses courriel se transforment en liens automatiquement.