Convertir une Exception en Response dans Symfony2
La méthode traditionnelle pour retourner une page avec le framework Symfony2 est
de retourner un objet Response
. Toutefois on peut remarquer que les pages
d’erreurs (notamment les 404) ne suivent pas ce principe. En effet pour
déclencher un code d’erreur 404 et afficher la page correspondante, il suffit
de lever une Exception comme ceci :
Il peut parfois s’avérer intéressant de reproduire ce système pour des
Exceptions métiers dans son projet. Supposons par exemple que nous devons gérer
des verrous sur l’édition d’un objet. C’est une demande fréquente sur des
applications web (intranet par exemple) où lorsqu’un utilisateur se trouve sur
le formulaire d’édition d’un objet, les autres utilisateurs, qui tentent
d’accéder à ce même formulaire, doivent être bloqués et notifiés de l’édition
en cours d’un utilisateur. Chez Lexik, nous avons récemment eu cette demande et
nous l’avons traité via une Exception
.
L’article suivant ne donnera pas une implémentation complète du bundle de
gestion des verrous d’édition, que nous appellerons LockBundle
tout au long de
l’article, mais servira d’exemple pour montrer comment transformer une
Exception
dans Symfony2 en Response
. Si vous êtes intéressés pour avoir une
version Open Source du LockBundle
, il suffit de le demander ;-)
Supposons que nous avons un service lock_manager
capable d’ajouter et
supprimer un verrou, ainsi que savoir si un objet est verrouillé ou non. La
seule contrainte est que l’objet passé en paramètre doit implémenter l’interface
LockableInterface
.
Dans un contexte classique, depuis un Controller Symfony2, il faudrait retourner
une Response
avec une vue attachée, comme ceci :
Si cette action doit être réalisée à plusieurs reprises, depuis plusieurs
Controller, ce code peut s’avérer rébarbatif et verbeux. Le plus simple serait
d’appeler seulement la méthode verify
de notre service et lever l’Exception
si un verrou est présent. Par contre, dans ce contexte, c’est une page 500 qui
s’affichera ce qui n’est pas acceptable.
Heureusement, Symfony2 et son composant HttpKernel
nous offre beaucoup de
souplesse et dispose d’un évènement lorsqu’une Exception est levée et non
catchée. Il s’agit de l’évènement kernel.exception
comme sa documentation
l’indique this event allows you to create a response for a thrown exception
.
Il nous suffit de créer un listener :
Il ne reste plus qu’à la définir en tant que service et l’associer à l’évènement
kernel.exception
:
Avec ce listener, nous pouvons maintenant simplifier le code de notre Controller comme ceci :
C’est très confortable car ce code peut être appelé depuis Controller ou même depuis classe modèle/service et le rendu pour l’utilisateur sera le même.
Nous pouvons même enrichir la Response
du listener pour retourner un status
code HTTP d’erreur (comme ce qui est fait pour les 404 par exemple). Pour cela
il suffit d’éditer la classe LockedExceptionListener
comme ceci :