IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Tutoriel pour apprendre à utiliser le framework Laravel 5.3

Plus loin


précédentsommairesuivant

VI. 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 inclut 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 déjà longue !

L'application d'exemple utilise les notifications pour l'envoi d'emails pour :

  • la confirmation de l'adresse email lors de l'enregistrement ;
  • le renouvellement du mot de passe.

Elle utilise également les notifications en base de données pour prévenir les rédacteurs que de nouveaux commentaires ont été ajoutés à leurs articles.

VI-A. Organisation du code

Les notifications se trouvent dans le dossier app/Notifications :

Image non disponible

Ce dossier n'existe pas dans l'installation de base de Laravel, il est ajouté lorsque l'on crée la première notification avec Artisan qui dispose à cet effet d'une commande :

Image non disponible

VI-B. La confirmation de l'adresse email

Pour voir comment fonctionnent les notifications on va voir en détail la confirmation de l'adresse email de l'application d'exemple.

Par défaut cette confirmation n'est pas prévue dans Laravel. Du coup lorsque quelqu'un s'enregistre en indiquant une adresse email on n'est pas sûr qu'elle soit valide.

VI-B-1. Le contrôleur RegisterController

Par défaut le contrôleur App\Http\Controllers\Auth\RegisterController contient ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
<?php
 
namespace App\Http\Controllers\Auth;
 
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\RegistersUsers;
 
class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */
 
    use RegistersUsers;
 
    /**
     * Where to redirect users after login / registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';
 
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }
 
    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|min:6|confirmed',
        ]);
    }
 
    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
}

Autrement dit toute la logique pour l'enregistrement se fait dans le framework, dans le trait lluminate\Foundation\Auth\RegistersUsers. Pour modifier cette logique on doit surcharger la méthode correspondante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
public function register(Request $request)
{
    $this->validator($request->all())->validate();
 
    $this->guard()->login($this->create($request->all()));
 
    return redirect($this->redirectPath());
}

Ici on trouve :

  • la validation ;
  • la création de l'utilisateur et sa connexion ;
  • une redirection.

On veut ajouter la validation de l'adresse email et évidemment supprimer la connexion automatique. Voici la méthode qui surcharge celle du trait :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public function register(RegisterRequest $request, UserRepository $userRepository)
{
    $user = $userRepository->store(
        $request->all(),
        str_random(30)
    );
 
    $this->notifyUser($user);
 
    return redirect('/')->with('ok', trans('front/verify.message'));
}

On voit que :

  • la validation se fait maintenant avec une requête de formulaire (ce n'était pas indispensable mais homogène par rapport à toutes les validations de l'application) ;
  • on envoie une notification (notify) créée par la classe ConfirmEmail ;
  • on redirige sur la page d'accueil du site avec un message localisé.

Remarquez que l'on ajoute aux données du formulaire d'enregistrement un code de confirmation (une chaîne aléatoire de 30 caractères).

La partie qui nous intéresse est celle de la notification. Pour que ça fonctionne le modèle doit utiliser le trait Notifiable, ce qui est par défaut le cas du modèle User :

 
Sélectionnez
class User extends Authenticatable
{
    use Notifiable;

VI-B-2. La notification

Voici la classe App\Notifications\ConfirmEmail :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
<?php
 
namespace App\Notifications;
 
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
 
class ConfirmEmail extends Notification implements ShouldQueue
{
    use Queueable;
 
    /**
     * The password reset token.
     *
     * @var string
     */
    public $confirmation_code;
 
    /**
     * Create a notification instance.
     *
     * @param  string  $confirmation_code
     * @return void
     */
    public function __construct($confirmation_code)
    {
        $this->confirmation_code = $confirmation_code;
    }
     
    /**
     * 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)
            ->subject(trans('front/verify.email-title'))
            ->line(trans('front/verify.email-title'))
            ->line(trans('front/verify.email-intro'))
            ->action(trans('front/verify.email-button'), url('confirm/' . $this->confirmation_code));
    }
}

On définit le canal de notification (via), ici mail (ça pourrait être database, broadcast, nexmo ou slack).

La méthode toMail définit le contenu de l'email. Il existe un lot de méthodes pratiques :

  • subject : pour le sujet de l'email ;
  • line : pour ajouter une ligne de texte :
  • action : pour ajouter un bouton de lien ;

Laravel est équipé d'un système de file d'attente (queue) qui permet de différer les tâches consommatrices de ressources, comme l'envoi d'un email. Ici c'est mis en œuvre avec l'interface ShouldQueue et le trait Queueable). Je ne développe pas cet aspect dans ce cours. Vous pouvez trouver la documentation ici.

VI-B-3. L'email

Voici l'email résultant en français :

Image non disponible

Vous avez une mise en forme automatique !

Par défaut le template est dans le framework. Mais si on doit le modifier, comme c'est le cas pour l'application d'exemple pour la localisation, il existe une commande d'Artisan pour le publier :

 
Sélectionnez
php artisan vendor:publish --tag=laravel-notifications

On trouve le template ici :

Image non disponible

Il devient ainsi accessible et modifiable.

VI-C. La notification des rédacteurs pour les nouveaux commentaires

On peut utiliser les notifications en base de données. Elles sont ainsi stockées en attendant d'être lues.

VI-C-1. La table des notifications

Pour que ça fonctionne il faut ajouter une table avec cette commande d'Artisan :

 
Sélectionnez
php artisan notifications:table

Ce qui a pour effet d'ajouter une migration :

Image non disponible

Après avoir effectué la migration on se retrouve avec cette table notifications :

Image non disponible

C'est dans cette table que sont stockées les notifications.

VI-C-2. La notification

Comme pour les emails il nous faut une classe de notification :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
<?php
 
namespace App\Notifications;
 
use Illuminate\Notifications\Notification;
use App\Models\Post;
 
class Commented extends Notification
{
    /**
     * Post property.
     *
     * @var \App\User\Post
     */
    protected $post;
 
    /**
     * Create a new notification instance.
     *
     * @param \App\Models\Post
     * @return void
     */
    public function __construct(Post $post)
    {
        $this->post = $post;
    }
 
    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['database'];
    }
 
    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            'title' => $this->post->title,
            'slug' => $this->post->slug,
        ];
    }
}

Cette fois dans la méthode via on a database.

On voit que l'on doit transmettre dans le constructeur une instance de l'article concerné ($post). La mise en forme se fait dans la méthode toArray où on retourne un tableau de données qui sera transformé en JSON dans la base.

On va ainsi transmettre le titre et le slug (pour générer l'URL) de l'article.

VI-C-3. Création d'une notification

Une notification sera créée quand un commentaire le sera. Voici la méthode store du contrôleur des commentaires (App\Http\Controllers\CommentController) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
public function store(CommentRequest $request, BlogRepository $blogRepository)
{
    $this->commentRepository->store($request->all(), $request->user()->id);
 
    $blog = $blogRepository->getById($request->post_id);
     
    $blog->user->notify(new Commented($blog));
     
    if (!$request->user()->valid) {
        $request->session()->flash('warning', trans('front/blog.warning'));
    }
 
    return back();
}

La ligne qui crée la notification est celle-ci :

 
Sélectionnez
$blog->user->notify(new Commented($blog));

Exactement comme on l'a vu pour les email ci-dessus.

On trouve cet enregistrement dans la table des notifications :

Image non disponible

La clé étrangère qui permet de connaître l'utilisateur est notifiable_id.

On a le type de notification pour les distinguer si on en a plusieurs, ce qui n'est pas le cas de l'application d'exemple. La colonne read_at est à NULL parce que la notification n'a pas encore été lue. Dans la colonne data on retrouve sous forme de JSON les informations transmises (title et slug).

VI-C-4. Les routes et le contrôleur

On a deux routes :

 
Sélectionnez
Route::get('notifications/{user}', 'NotificationController@index');
Route::put('notifications/{notification}', 'NotificationController@update');

La première est pour l'affichage des notifications.

La seconde est pour marquer une notification comme lue.

Voici le contrôleur (App\Http\Controllers\NotificationController) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Notifications\DatabaseNotification;
 
class NotificationController extends Controller
{
    /**
     * Create a new NotificationController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('redac');
    }
 
    /**
     * Display a listing notifications.
     * 
     * @param  \App\Models\User $user
     * @return \Illuminate\Http\Response
     */
    public function index(User $user)
    {
        return view('back.notifications.index', compact('user'));
    }
 
    /**
     * Update the notification.
     *
     * @param  Illuminate\Http\Request $request
     * @param  integer $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, DatabaseNotification $notification)
    {
        $notification->markAsRead();
 
        if($request->user()->unreadNotifications->isEmpty()) {
            return redirect()->route('blog.index');
        }
  
        return back();
    }
}

On applique le middleware redac pour que les méthodes ne soient accessibles qu'aux rédacteurs.

La première méthode (index) ne pose pas de problème puisqu'on se contente d'utiliser une vue en transmettant une instance du modèle pour l'utilisateur connecté.

La seconde méthode (update) est destinée à marquer une notification comme lue, ce qui se fait avec la méthode markAsRead.

Remarquez que pour les deux méthodes on utilise la liaison automatique entre le paramètre de la route et un modèle. De cette manière on a directement une instance du modèle dans la méthode.

VI-C-5. La vue index

Voici la vue pour afficher les notifications (resources/views/back/notifications/index.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
@extends('back.template')
 
@section('main')
 
  @include('back.partials.entete', ['title' => trans('back/notifications.notifications'), 'icon' => 'bell', 'fil' => trans('back/notifications.new-notifications')])
 
  <div class="row col-lg-12">
    <div class="table-responsive">
      <table class="table">
        <thead>
          <tr>
            <th>{!! trans('back/notifications.post') !!}</th>
            <th>{!! trans('back/notifications.date') !!}</th>
            <th>{!! trans('back/notifications.valid') !!}</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          @foreach($user->unreadNotifications as $notification)
                <tr>
                  <td>{!! link_to('blog/' . $notification->data['slug'], $notification->data['title']) !!}</td>
                  <td>{{ formatDate($notification->created_at) }}</td> 
                  <td>{!! Form::checkbox(trans('valid'), null, $user->valid, ['disabled' => true]) !!}</td>
                  <td>
                    {!! Form::open(['method' => 'PUT', 'url' => 'notifications/' . $notification->id]) !!}
                      {!! Form::submit(trans('back/notifications.erase'), ['class' => 'btn btn-success btn-block']) !!}
                    {!! Form::close() !!}
                  </td>
                </tr>
          @endforeach
        </tbody>
      </table>
    </div>
  </div>
 
@endsection

On voit que l'on peut récupérer toutes les notifications non lues de l'utilisateur avec $user->unreadNotifications.

Voici l'aspect de la vue :

Image non disponible

Si on clique sur « Effacer » on appelle la méthode update du contrôleur vue ci-dessus et la notification est marquée comme lue (mise à jour de la colonne read_at) et elle disparaît de l'affichage.

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

VI-D. 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.
  • On peut stocker les notifications en base de données pour une utilisation ultérieure.

précédentsommairesuivant

Copyright © 2017 Laravel. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.