Laravel 8

Cours Laravel 8 – les notifications

On a vu dans ce cours comment envoyer un email avec Laravel. Mais on dispose aussi d’un système complet de notifications, par exemple par SMS, qui inclue aussi les emails ou même la base de données.

Classiquement une notification est un message court pour informer un utilisateur qu’il s’est passé quelque chose qui le concerne dans l’application.

Par exemple une donnée sensible a été mise à jour, on envoie un SMS par sécurité en informant l’utilisateur de ce changement et, si ce n’est pas lui qui l’a effectué, il peut alors intervenir.

Évidemment pour tout ce qui n’est pas email ou base de données il faut utiliser un service externe. Il y a un site dédié pour tous les drivers existants et la liste est longue !

Organisation du code

Il y a une commande d’artisan pour créer une notification :

php artisan make:notification MaNotification

Les notifications se trouvent dans le dossier app/Notifications :

Ce dossier n’existe pas dans l’installation de base de Laravel, il est ajouté lorsqu’on crée la première notification avec Artisan.

Évidemment pour les packages les notifications se situent dans un dossier du package.

Le renouvellement du mot de passe

On a vu en détail le renouvellement du mot de passe dans ce chapitre. J’ai alors précisé qu’on envoyait un email par le système de notification en précisant que je vous en parlerai plus tard. C’est ce que je vais faire maintenant.

On a vu que Laravel, après la demande de renouvellement de l’utilisateur, expédie ce genre d’email :

Par défaut la classe de notification se situe dans le framework :

Avec ce code :

<?php

namespace Illuminate\Auth\Notifications;

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Lang;

class ResetPassword extends Notification
{
    /**
     * The password reset token.
     *
     * @var string
     */
    public $token;

    /**
     * The callback that should be used to create the reset password URL.
     *
     * @var \Closure|null
     */
    public static $createUrlCallback;

    /**
     * The callback that should be used to build the mail message.
     *
     * @var \Closure|null
     */
    public static $toMailCallback;

    /**
     * Create a notification instance.
     *
     * @param  string  $token
     * @return void
     */
    public function __construct($token)
    {
        $this->token = $token;
    }

    /**
     * Get the notification's channels.
     *
     * @param  mixed  $notifiable
     * @return array|string
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Build the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable, $this->token);
        }

        if (static::$createUrlCallback) {
            $url = call_user_func(static::$createUrlCallback, $notifiable, $this->token);
        } else {
            $url = url(route('password.reset', [
                'token' => $this->token,
                'email' => $notifiable->getEmailForPasswordReset(),
            ], false));
        }

        return (new MailMessage)
            ->subject(Lang::get('Reset Password Notification'))
            ->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
            ->action(Lang::get('Reset Password'), $url)
            ->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
            ->line(Lang::get('If you did not request a password reset, no further action is required.'));
    }

    /**
     * Set a callback that should be used when creating the reset password button URL.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function createUrlUsing($callback)
    {
        static::$createUrlCallback = $callback;
    }

    /**
     * Set a callback that should be used when building the notification mail message.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function toMailUsing($callback)
    {
        static::$toMailCallback = $callback;
    }
}

Comme c’est une notification par email on a une méthode toMail. D’autre part on précise dans la méthode via qu’on retourne un email. La classe MailMessage offre un certain nombre de méthodes bien pratiques comme :

  • line : pour écrire une ligne
  • action : pour afficher un bouton
  • from : pour définir l’adresse de l’expéditeur
  • subject : pour définir le sujet
  • cc et bcc : pour faire des copies
  • attach : pour joindre un document
  • priority : pour fixer la priorité…

Vous constatez qu’on a une mise en forme de l’email plutôt esthétique. cette mise en forme est issue d’une vue par défaut dans le framework :

Si vous voulez modifier cette mise en forme il faut publier cette vue :

php artisan vendor:publish --tag=laravel-notifications

Vous retrouvez alors la vue dans le dossier resources/views/vendor/notifications :

Vous pouvez vous demander maintenant comment est effectivement commandée cette notification. Ça se passe dans le trait CanResetPassword avec la méthode notify :

<?php

namespace Illuminate\Auth\Passwords;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;

trait CanResetPassword
{
    ...

    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }
}

Un exemple

Comme rien ne vaut un exemple on va en réaliser un. On va à nouveau partir d’une installation vierge de Laravel avec Jetstream. On va décider d’envoyer un email à un utilisateur qui s’enregistre avec succès. On commence par créer la notification avec Artisan :

php artisan make:notification RegisteredNotification

On a ce code à la création :

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class RegisteredNotification extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('The introduction to the notification.')
                    ->action('Notification Action', url('/'))
                    ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

On va modifier la fonction toMail pour changer le message :

public function toMail($notifiable)
{
    return (new MailMessage)->line('Vous avez bien été enregistré sur notre site !');
}

Il ne reste plus qu’à envoyer la notification à partir de l’action CreateNewUser :

use App\Notifications\RegisteredNotification;

...

protected function create(array $data)
{
    ...
    $user = User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
    ]);

    $user->notify(new RegisteredNotification());

    return $user;
}

Maintenant quand un utilisateur s’enregistre il reçoit un email :

C’est tout simple !

Si on veut le texte de fond en français, ou tout autre langage on peut publier le template comme on l’a vu plus haut :

Vous pouvez d’ailleurs remarquer que ce template est déjà prévu i18n. Par exemple pour l’accueil on a :

@lang('Hello!')

Autrement dit, comme on l’a vu dans l’article précédent sur la localisation, il suffit de prévoir par exemple un fichier fr.json :

Avec ce code :

{
    "Hello!": "Bonjour !"
}

Et maintenant dans l’email :

 

 

 

Il y aurait encore beaucoup à dire sur les notifications au-delà des exemples de ce chapitre, reportez-vous à la documentation officielle pour en savoir plus.

En résumé

  • Laravel comporte un système complet et performant de notifications.
  • On peut envoyer des emails avec les notifications, leur mise en forme est facilitée par la présence de puissantes méthodes et d’un template qu’on peut personnaliser.
Print Friendly, PDF & Email

8 commentaires

  • fabBlab

    Bonjour,

    Il y a un des codes fournis qui est à actualiser :
    protected function create(array $data)
    {

    $user = User::create([
    ‘name’ => $data[‘name’],
    ’email’ => $data[’email’],
    ‘password’ => Hash::make($data[‘password’]),
    ]);

    $user->notify(new RegisteredNotification());

    return $user;
    }
    Il suffit juste de remplacer $data par $input et c’est ok.
    Ou bien de faire l’inverse pour le nom du paramètre passé à la méthode create(), mais la première option me semble meilleure.

    Sinon, pour remplacer les textes en anglais, on peut utiliser d’autres méthodes MailMessage.
    Exemple :
    public function toMail($notifiable)
    {
    return (new MailMessage)
    ->subject(« Bienvenue ! »)
    ->greeting(« Bienvenue ! »)
    ->line(« Vous avez bien été enregistré sur notre site ! »)
    ->salutation(« À bientôt. »);
    }
    Cela ne sort pas de mon chapeau 🙂 J’ai trouvée ces variables dans la vue email.blade.php
    C’est juste pour l’exemple, car les textes devraient se trouver dans leurs propres fichiers pour éviter de les mélanger avec le code, faciliter les traductions, etc.

  • rodriemo

    Bonjour bestmomo,
    merci pour ces cours édifiants qui me permet de découvrir le ministère du développement.
    SVP, j’ai besoin de votre aide, je suis sur un projet test pour apprendre laravel. il s’agit d’un projet de gestion de recrutement.
    en effet j’ai 4 tables une table candidates, une table experiences, une table computers et une table competences.
    il s’agit d’une relation 1:n entre la table candidates et les autres. on trouve donc la clé secondaire candidate_id dans toute les trois autres tables. signalons que le candidat doit se connecter avec son compte enregistrer dans la table users. j’aimerais me basé sur le user_id pour retouver le candidate_id et l’insérer dans les autres tables. l’enregistrement se fait en bloc sur une vue.
    voici mon controleur
    experienceRepository = $experienceRepository;
    $this->competenceRepository = $competenceRepository;
    $this->computerRepository = $computerRepository;
    $this->middleware('auth');

    }

    /**
    * Display a listing of the candidates.
    *
    * @return \Illuminate\Http\Response
    */
    public function index()
    {
    $candidate = Candidate::all();
    $experiences = $this->experienceRepository->getPaginate($this->nbrePerpage);
    return view('back.recruit.candidates.index', compact('candidates'));
    }

    /**
    * Show the form for creating a new resource.
    *
    * @return \Illuminate\Http\Response
    */
    public function create()
    {
    $candidate = Candidate::all();
    return view('front.candidates.experience');
    }

    /**
    * Store a newly created resource in storage.
    *
    * @param \Illuminate\Http\Request $request
    * @return \Illuminate\Http\Response
    */
    public function store(ExperienceCreateRequest $request, CompetenceCreateRequest $request1, ComputerCreateRequest $request2 )
    {

    $inputs = array_merge($request->all(), ['candidate_id' => $request->candidate()->id->where('user_id', 'id')->user()->id]);

    $experience = $this->experienceRepository->store($inputs);

    $inputs1 = array_merge($request1->all(), ['candidate_id' => $request1->$candidate->id->where('user_id', 'id')->user()->id]);

    $competence = $this->competenceRepository->store($inputs1);

    $inputs2 = array_merge($request2->all(), ['candidate_id' => $request2->$candidate->id->where('user_id', 'id')->user()->id]);

    $computer = $this->computerRepository->store($inputs2);

    return redirect()->route('degrees.create')->withOk('success', 'Degree created successfully! Poursuivre avec vos diplômes');
    }

    public function show(Candidate $candidate)
    {
    return view('back.recruit.candites.show', compact('candidate'));
    }

    public function edit(Candidate $candidate)
    {
    return view('front.candidates.edit', compact('candidate'));
    }

    /**
    * Update the specified Candidate in storage.
    *
    * @param \Illuminate\Http\Request $request
    * @param int $id
    * @return \Illuminate\Http\Response
    */
    public function update()
    {

    }
    /**
    * Remove the specified contact from storage.
    *
    * @param int $id
    * @return \Illuminate\Http\Response
    */
    public function destroy()
    {

    }

    }
    lors d’enregistre sur ma vue, j’ai le message d’erreur: Undefined variable: candidate.
    merci de m’apporter votre expertise car je suis vraiment un bleu dans POO.
    merci d’avance

    • bestmomo

      Bonjour,

      Merci d’apprécier mes cours !

      Mais les commentaires sont faits pour poser des questions concernant l’article concerné, là il s’agit d’une question traitant d’un projet spécifique. Il vaudrait mieux poser ce genre de question sur le forum https://laravel.fr sur lequel j’interviens également et qui est plus adapté à un échange. La question posée réclame des précisions concernant le contexte d’apparition de l’erreur.

Laisser un commentaire