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 :
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 :
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 :
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 :
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 :
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 :
class User extends
Authenticatable
{
use
Notifiable;
VI-B-2. La notification▲
Voici la classe App\Notifications\ConfirmEmail :
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 :
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 :
php artisan vendor:publish --tag=laravel-notifications
On trouve le template ici :
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 :
php artisan notifications:table
Ce qui a pour effet d'ajouter une migration :
Après avoir effectué la migration on se retrouve avec cette table notifications :
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 :
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) :
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 :
Exactement comme on l'a vu pour les email ci-dessus.
On trouve cet enregistrement dans la table des notifications :
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 :
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) :
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) :
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 :
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.