I. Introduction▲
La notion de middleware (logiciel médiateur, intergiciel) est nouvelle dans le monde de Laravel. Il faut le voir comme une couche entre la requête et l'application. Voici une représentation pour aider à la compréhension :
Le but d'un middleware est donc d'agir sur la requête ou d'y lire des informations pour adapter la configuration de l'application en conséquence. L'application utilise trois middlewares spécifiques comme nous allons le voir dans ce tutoriel.
II. Les routes▲
Voici la totalité des routes obtenues avec la commande php artisan route:list :
Vous pouvez cliquer sur l'image pour en obtenir un agrandissement.
Voici le fichier app/Http/route.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.
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.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
<?php
Route::
group([
'middleware'
=>
[
'web'
]],
function
() {
// Home
Route::
get('/'
,
[
'uses'
=>
'HomeController@index'
,
'as'
=>
'home'
]
);
Route::
get('language/{lang}'
,
'HomeController@language'
)->
where('lang'
,
'[A-Za-z_-]+'
);
// Admin
Route::
get('admin'
,
[
'uses'
=>
'AdminController@admin'
,
'as'
=>
'admin'
,
'middleware'
=>
'admin'
]
);
Route::
get('medias'
,
[
'uses'
=>
'AdminController@filemanager'
,
'as'
=>
'medias'
,
'middleware'
=>
'redac'
]
);
// Blog
Route::
get('blog/order'
,
[
'uses'
=>
'BlogController@indexOrder'
,
'as'
=>
'blog.order'
]
);
Route::
get('articles'
,
'BlogController@indexFront'
);
Route::
get('blog/tag'
,
'BlogController@tag'
);
Route::
get('blog/search'
,
'BlogController@search'
);
Route::
put('postseen/{id}'
,
'BlogController@updateSeen'
);
Route::
put('postactive/{id}'
,
'BlogController@updateActive'
);
Route::
resource('blog'
,
'BlogController'
);
// Comment
Route::
resource('comment'
,
'CommentController'
,
[
'except'
=>
[
'create'
,
'show'
]
]
);
Route::
put('commentseen/{id}'
,
'CommentController@updateSeen'
);
Route::
put('uservalid/{id}'
,
'CommentController@valid'
);
// Contact
Route::
resource('contact'
,
'ContactController'
,
[
'except'
=>
[
'show'
,
'edit'
]
]
);
// User
Route::
get('user/sort/{role}'
,
'UserController@indexSort'
);
Route::
get('user/roles'
,
'UserController@getRoles'
);
Route::
post('user/roles'
,
'UserController@postRoles'
);
Route::
put('userseen/{user}'
,
'UserController@updateSeen'
);
Route::
resource('user'
,
'UserController'
);
// Authentication routes...
Route::
get('auth/login'
,
'Auth\AuthController@getLogin'
);
Route::
post('auth/login'
,
'Auth\AuthController@postLogin'
);
Route::
get('auth/logout'
,
'Auth\AuthController@getLogout'
);
Route::
get('auth/confirm/{token}'
,
'Auth\AuthController@getConfirm'
);
// Resend routes...
Route::
get('auth/resend'
,
'Auth\AuthController@getResend'
);
// Registration routes...
Route::
get('auth/register'
,
'Auth\AuthController@getRegister'
);
Route::
post('auth/register'
,
'Auth\AuthController@postRegister'
);
// Password reset link request routes...
Route::
get('password/email'
,
'Auth\PasswordController@getEmail'
);
Route::
post('password/email'
,
'Auth\PasswordController@postEmail'
);
// Password reset routes...
Route::
get('password/reset/{token}'
,
'Auth\PasswordController@getReset'
);
Route::
post('password/reset'
,
'Auth\PasswordController@postReset'
);
}
);
Il y a des routes élémentaires avec les verbes get, put et post :
2.
3.
4.
5.
6.
7.
8.
Route::
get('
/
'
,
[
'
uses
'
=>
'
HomeController@index
'
,
'
as
'
=>
'
home
'
]
);
Route::
put('
postseen/{id}
'
,
'
BlogController@updateSeen
'
);
Route::
post('
user/roles
'
,
'
UserController@postRoles
'
);
On a enfin des ressources pour les articles, les commentaires, les contacts et les utilisateurs :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Route::
resource('
blog
'
,
'
BlogController
'
);
Route::
resource('
comment
'
,
'
CommentController
'
,
[
'
except
'
=>
[
'
create
'
,
'
show
'
]
]
);
Route::
resource('
contact
'
,
'
ContactController
'
,
[
'
except
'
=>
[
'
show
'
,
'
edit
'
]
]
);
Route::
resource('
user
'
,
'
UserController
'
);
Pour mémoire, la commande resource permet de créer toutes les routes RestFull. Si on regarde par exemple pour le blogue avec la simple ligne prévue, on se retrouve avec toutes ces routes :
On bénéficie en plus du nommage standardisé de ces routes.
III. Les middlewares▲
Il est prévu trois middlewares pour l'application :
III-A. isAdmin▲
Ce middleware a pour but de vérifier que l'utilisateur connecté est un administrateur, ce qui sera utile pour autoriser l'accès à la zone d'administration. En voici le 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.
<?php
namespace
App\Http\Middleware;
use
Closure;
use
Illuminate\Http\RedirectResponse;
class
IsAdmin {
/**
* Handle an incoming request.
*
*
@param
\Illuminate\Http\Request
$request
*
@param
\Closure
$next
*
@return
mixed
*/
public
function
handle($request
,
Closure $next
)
{
$user
=
$request
->
user();
if
($user
&&
$user
->
isAdmin())
{
return
$next
($request
);
}
return
new
RedirectResponse(url('/'
));
}
}
En gros un middleware reçoit la requête, effectue son traitement, puis transmet la requête au middleware suivant, ou à l'application si c'est le dernier.
Ici on utilise la méthode isAdmin qu'on a prévue dans le modèle User (que nous avons vue lors du précédent tutoriel). Si c'est un administrateur on continue de propager la requête. Dans le cas contraire on redirige sur la page d'accueil.
Ce middleware est appliqué dans la route qui dirige vers l'administration :
2.
3.
4.
5.
Route::
get('
admin
'
,
[
'
uses
'
=>
'
AdminController@admin
'
,
'
as
'
=>
'
admin
'
,
'
middleware
'
=>
'
admin
'
]
);
Mais on le retrouve aussi dans certains contrôleurs comme CommentController, appliqué à certaines méthodes réservées aux administrateurs :
$this
->
middleware('
admin
'
,
[
'
except
'
=>
[
'
store
'
,
'
edit
'
,
'
update
'
,
'
destroy
'
]]
);
Pour voir rapidement partout où il est utilisé, il suffit de regarder le tableau généré par php artisan route:list que nous avons vu ci-dessus.
III-B. isRedactor▲
Ce middleware ressemble beaucoup au précédent. Le but est de savoir si l'utilisateur connecté est au moins un rédacteur (parce qu'il est évident qu'un administrateur aura aussi les droits correspondants). Voici le 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.
<?php
namespace
App\Http\Middleware;
use
Closure;
use
Illuminate\Http\RedirectResponse;
class
IsRedactor {
/**
* Handle an incoming request.
*
*
@param
\Illuminate\Http\Request
$request
*
@param
\Closure
$next
*
@return
mixed
*/
public
function
handle($request
,
Closure $next
)
{
$user
=
$request
->
user();
if
($user
&&
$user
->
isNotUser())
{
return
$next
($request
);
}
return
new
RedirectResponse(url('/'
));
}
}
Cette fois on utilise la méthode isNotUser du modèle User. En effet, si ce n'est pas un simple utilisateur c'est soit un rédacteur, soit un administrateur, donc exactement ce qu'on veut.
Ce middleware est utilisé dans la route des médias :
2.
3.
4.
5.
Route::
get('
medias
'
,
[
'
uses
'
=>
'
AdminController@filemanager
'
,
'
as
'
=>
'
medias
'
,
'
middleware
'
=>
'
redac
'
]
);
En effet seuls les rédacteurs et les administrateurs sont autorisés à gérer les médias.
On trouve aussi ce middleware dans le constructeur de certains contrôleurs comme dans BlogController pour certaines fonctions :
$this
->
middleware('
redac
'
,
[
'
except
'
=>
[
'
indexFront
'
,
'
show
'
,
'
tag
'
,
'
search
'
]]
);
Pour voir rapidement partout où il est utilisé, comme nous l'avons vu pour le précédent middleware, il suffit de regarder le tableau généré par php artisan route:list, que nous avons vu ci-dessus.
III-C. isAjax▲
Ce middleware ne concerne pas les rôles comme les deux précédents, mais la nature de la requête. On veut vérifier que celle-ci est en Ajax. Voici le code :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<?php
namespace
App\Http\Middleware;
use
Closure;
class
IsAjax {
public
function
handle($request
,
Closure $next
)
{
if
($request
->
ajax())
{
return
$next
($request
);
}
abort(404
);
}
}
La méthode ajax de la classe Request permet facilement de le vérifier. Si la requête n'est pas en Ajax on affiche une erreur 404. Laravel 5 permet facilement d'utiliser une vue spécifique pour cette erreur, il suffit de la placer dans resources/views/errors et de la nommer correctement :
III-D. Déclaration des middlewares▲
Il ne suffit pas de créer un middleware pour qu'il soit connu par Laravel, il faut le déclarer dans app/Http/Kernel.php :
2.
3.
4.
5.
6.
7.
8.
protected $routeMiddleware
=
[
'
auth
'
=>
\App\Http\Middleware\Authenticate::
class,
'
auth.basic
'
=>
\Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::
class,
'
guest
'
=>
\App\Http\Middleware\RedirectIfAuthenticated::
class,
'
admin
'
=>
\App\Http\Middleware\IsAdmin::
class,
'
redac
'
=>
\App\Http\Middleware\IsRedactor::
class,
'
ajax
'
=>
\App\Http\Middleware\IsAjax::
class
];
On trouve ici les trois middlewares ajoutés à Laravel.
IV. Les commandes▲
La version 5 de Laravel a également vu l'arrivée des commandes. Non pas celles d'Artisan que nous avions déjà, mais un nouveau concept qui permet de gérer différemment le code si on le désire. De quoi s'agit-il ?
Le « command bus » de Laravel est l'application d'un pattern bien connu. Il consiste à séparer les actions à effectuer en tâches élémentaires. Pour chacune d'elles, vous créez un objet de commande que vous envoyez dans le « bus » et ensuite vous savez que la commande aboutira et sera exécutée.
Il y a un dossier appelé Jobs dédié à ces commandes :
Dans notre application j'en ai prévu deux pour gérer la localisation. Regardons le code de SetLocale :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<?php
namespace
App\Jobs;
use
App\Jobs\Job;
use
Request;
class
SetLocale extends
Job
{
/**
* Execute the command.
*
*
@return
void
*/
public
function
handle()
{
if
(!
session()->
has('locale'
))
{
session()->
put('locale'
,
Request::
getPreferredLanguage( config('app.languages'
) ));
}
app()->
setLocale(session('locale'
));
}
}
On fait une action simple : définir la locale selon l'utilisateur. Si on a déjà une valeur dans la session, on l'utilise, sinon on pioche l'information dans la requête avec la méthode getPreferredLanguage. Ensuite on définit la locale de l'application.
La seconde commande est encore plus simple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<?php
namespace
App\Jobs;
use
App\Jobs\Job;
use
Illuminate\Contracts\Bus\SelfHandling;
class
ChangeLocale extends
Job implements
SelfHandling
{
/**
* Execute the job.
*
*
@return
void
*/
public
function
handle()
{
session()->
set('locale'
,
$this
->
lang);
}
}
Ici on se contente de changer la locale.
Maintenant la question est : d'où appelle-t-on ces commandes ? Regardez le middleware App :
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.
<?php
namespace
App\Http\Middleware;
use
Closure;
use
App\Jobs\SetLocale;
use
Illuminate\Bus\Dispatcher as
BusDispatcher;
use
App\Events\UserAccess;
class
App
{
/**
* The command bus.
*
* @array $bus
*/
protected
$bus
;
/**
* The command bus.
*
* @array $bus
*/
protected
$setLocale
;
/**
* Create a new App instance.
*
*
@param
Illuminate\Bus\Dispatcher
$bus
*
@param
App\Jobs\SetLocaleCommand
$setLocaleCommand
*
@return
void
*/
public
function
__construct
(
BusDispatcher $bus
,
SetLocale $setLocale
)
{
$this
->
bus =
$bus
;
$this
->
setLocale =
$setLocale
;
}
/**
* Handle an incoming request.
*
*
@param
Illuminate\Http\Request
$request
*
@param
Closure
$next
*
@return
mixed
*/
public
function
handle($request
,
Closure $next
)
{
$this
->
bus->
dispatch($this
->
setLocale);
event(new
UserAccess);
return
$next
($request
);
}
}
Ce middleware App est présent à l'installation de Laravel 5. C'est un bon emplacement pour faire des initialisations. Ici on va lancer la commande setLocale pour initialiser la locale lorsqu'une requête arrive. Vous pouvez voir comme c'est simplement réalisé.
Remarquez au passage qu'on déclenche aussi l'événement UserAccess. Nous verrons dans un chapitre ultérieur les événements. Ici nous fixerons grâce à cet événement le statut de l'utilisateur selon son rôle.
La commande ChangeLocale n'est évidemment pas utilisée au démarrage de l'application, mais uniquement lorsque l'utilisateur désire changer la langue de l'interface. Pour ça il dispose d'un petit drapeau dans la barre de menu :
Ce drapeau change d'aspect selon la langue actuelle. Si vous regardez dans le contrôleur HomeController vous trouvez le code correspondant :
C'est ici qu'on utilise cette commande pour changer la locale.
J'ai réalisé cette partie avec des commandes, mais j'aurais pu le faire avec un service, ou d'une autre manière encore. Laravel permet de réaliser une application de plusieurs façons selon les goûts de chacun !