Objectif
Le but de cet article est de proposer un exemple de page de connexion.
Il s'articule autour de deux fichiers principaux : un template Twig et un contrôleur PHP.
Il est réalisé entièrement en mode Symfony, sans utiliser le stack Legacy.
Si l'utilisateur saisit de mauvais identifiants, un message d'erreur est affiché.
Une fois connecté, il est redirigé vers la page qu'il consultait avant de se connecter.
Prérequis
- Vous avez déjà créé le Bundle
Acme/MyBundle
, et l'avez activé dans le fichier
ezpublish/EzPublishKernel.php
.
- Le pagelayout utilisé par défaut est
AcmeMyBundle::pagelayout.html.twig
.
Il possède un bloc nommé col_main
.
- Dans le pagelayout, la variable
redirect_uri
doit être définie et contenir l'url courante
(ex: /Ma-rubrique/Mon-article
).
Création du template
Dans le répertoire de templates de votre bundle (Resources/views/
),
créez les répertoires user/connection/
, qui contiendront tous les templates pour la connexion des utilisateurs
(connexion, inscription, ...)
Créez ensuite le fichier login.html.twig
dans le répertoire user/connection/
.
Voici à quoi il peut ressembler :
{# Surcharge du bloc 'col_main' du template pagelayout.html.twig, pour la page de connexion #}
{% extends noLayout ? viewbaseLayout : "AcmeMyBundle::pagelayout.html.twig" %}
{#
Affiche un article en mode Full
Paramètres :
- noLayout : False
- fail_login : Si la connexion a échouée
- redirect_uri : URI vers laquelle rediriger après la connexion
#}
{% block col_main %}
<div class="main-content user user-login panel">
{# Titre #}
<header class="heading">
<h1>Connexion</h1>
</header>
{# Contenu #}
<div class="content-body">
{# Message d'erreur #}
{% if fail_login %}
<div class="alert alert-danger">
<button data-dismiss="alert" class="close" type="button">×</button>
<p><strong>Erreur !</strong></p>
<p>Identifiant ou mot de passe invalide.</p>
</div>
{% endif %}
{# Formulaire #}
<form class="form-horizontal" method="post"
action="{{ path( 'acme_my_user_login', { 'redirectURI': redirect_uri } ) }}">
<div class="form-group {% if fail_login %}has-error{% endif %}">
<label class="col-lg-3 control-label" for="login">Identifiant</label>
<div class="col-lg-4">
<input type="text" placeholder="Identifiant"
id="login" name="Login" class="form-control input-small">
</div>
<div class="validation col-lg-5">
<img src="{{ asset( "bundles/acmemy/images/error.png" ) }}" alt="Erreur" />
</div>
</div>
<div class="form-group {% if fail_login %}has-error{% endif %}">
<label class="col-lg-3 control-label" for="password">Mot de passe</label>
<div class="col-lg-4">
<input type="password" placeholder="Mot de passe"
id="password" name="Password" class="form-control input-small">
</div>
<div class="validation col-lg-5">
<img src="{{ asset( "bundles/acmemy/images/error.png" ) }}" alt="Erreur" />
</div>
</div>
<div class="form-group">
<div class="col-lg-10 text-right">
<button class="btn btn-primary" type="submit">Connexion</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
Explications :
- Ligne 2, on commence par étendre le pagelayout par défaut pour modifier le bloc principal.
- Juste en dessous, en commentaire, on liste les paramètres disponibles/nécessaires dans le template.
- Ensuite, on commence la surcharge du bloc principal de la page :
col_main
.
- On affiche un message d'erreur si les identifiants sont mauvais (
{% if fail_login %}
).
- On affiche un formulaire avec un champ identifiant et un champ mot de passe.
- Le formulaire pointe vers la route
acme_my_user_login
, sur laquelle Symfony va brancher le futur contrôleur.
L'URL courante lui sera transmise en argument.
Remarques :
- L'image d'erreur doit être présente dans le répertoire
Resources/public/images/
du bundle.
- Ce template utilise le framework CSS Bootstrap 3.
- Les textes devraient être entre les tags
{% trans %}
et {% endtrans %}
, pour pouvoir être traduits
facilement par la suite si besoin.
Création du contrôleur
C'est le contrôleur qui va gérer les actions de connexion et déconnexion des utilisateurs.
Il aura donc deux actions : login et logout.
Créez le fichier UserConnectionController.php
dans le répertoire Controller/
de votre Bundle :
<?php
namespace Acme\MyBundle\Controller;
use \eZ\Bundle\EzPublishCoreBundle\Controller;
use \eZ\Publish\API\Repository\Exceptions\NotFoundException;
use \eZ\Publish\API\Repository\Values\User\User;
use \Symfony\Component\HttpFoundation\Cookie;
use \Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Controleur pour gérer les vues de connexion des utilisateurs.
*/
class UserConnectionController extends Controller {
/**
* Gestion de l'affichage de la page de login.
* - Affiche la page de login par défaut
* - Affiche d'éventuelles erreur de connexion
* - Connecte et redirige l'utilisateur
*
* @param string URI vers laquelle rediriger après la déconnexion
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function loginAction( $redirectURI ) {
$failLogin = false;
// Suppression d'un éventuel doublon dans l'URI
if ( ( $offset = strpos( $redirectURI, '/user/login' ) ) === 0 ) {
$redirectURI = substr( $redirectURI, $offset );
}
$request = $this->getRequest()->request;
// Si le formulaire de connexion a été soumis
if ( $request->has( 'Login' ) && $request->has( 'Password' ) ) {
$login = $request->get( 'Login' );
$password = $request->get( 'Password' );
if ( trim( $login ) != '' && trim( $password ) != '' ) {
$userService = $this->getRepository()->getUserService();
try {
$user = $userService->loadUserByCredentials( $login, $password );
return $this->connectUser( $user, $redirectURI );
} catch (NotFoundException $e) {
$failLogin = true;
}
} else {
$failLogin = true;
}
}
return $this->render(
'AcmeMyBundle:user\connection:login.html.twig',
array(
'noLayout' => false,
'fail_login' => $failLogin,
'redirect_uri' => $redirectURI
)
);
}
/**
* Déconnecte l'utilisateur courant.
*
* @param string URI vers laquelle rediriger après la déconnexion
*
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException Si l'utilisateur anonyme n'existe pas
*/
public function logoutAction( $redirectURI ) {
// Suppression d'un éventuel doublon dans l'URI
if ( ( $offset = strpos( $redirectURI, '/user/logout' ) ) === 0 ) {
$redirectURI = substr( $redirectURI, $offset );
}
// Récupération de l'utilisateur anonyme
$userService = $this->getRepository()->getUserService();
$anonymousUser = $userService->loadAnonymousUser();
return $this->connectUser( $anonymousUser, $redirectURI );
}
/**
* Connecte l'utilisateur en argument et retourne une redirection 302.
* Si l'utilisateur en argument est anonyme, alors c'est une déconnexion.
*
* @param \eZ\Publish\API\Repository\Values\User\User $user L'utilisateur à connecter
* @param string $redirectURI URI vers laquelle rediriger l'utilisateur après connexion
*
* @return RedirectResponse
*/
protected function connectUser( User $user, $redirectURI = '/' ) {
$repository = $this->getRepository();
$repository->setCurrentUser( $user );
$session = $this->getRequest()->getSession();
try {
$response = new RedirectResponse( $redirectURI );
} catch (NotFoundException $e) {
$response = new RedirectResponse( '/' );
}
$userService = $repository->getUserService();
$anonymousUser = $userService->loadAnonymousUser();
// Si l'utilisateur en argument est anonyme
if ( $user->id === $anonymousUser->id ) {
// Déconnexion de l'utilisateur courant
$response->headers->setCookie( new Cookie( 'is_logged_in', 'false' ) );
$session->remove( 'eZUserLoggedInID' );
} else {
// Connexion de l'utilisateur
$response->headers->setCookie( new Cookie( 'is_logged_in', 'true' ) );
$session->set( 'eZUserLoggedInID', $user->id );
}
return $response;
}
}
Explications :
- Le contrôleur est une classe PHP qui étend la classe Controller du Bundle
eZ\Bundle\EzPublishCoreBundle
fourni par eZ Publish.
- Ses deux premières méthodes sont des actions et leurs noms finissent d'ailleurs par
Action
.
Elles retournent un objet Response
.
- La troisième est une méthode utilitaire pour connecter un utilisateur.
- Par défaut,
loginAction()
inclue le template de login dans sa réponse, grâce aux lignes :
return $this->render(
'AcmeMyBundle:modules/user:login.html.twig',
array(
'noLayout' => false,
'fail_login' => $failLogin,
'redirect_uri' => $redirectURI
)
);
- Si le formulaire est soumis et valide, on appelle la méthode
connectUser()
qui connecte l'utilisateur
et on fournit une réponse de type redirection.
Configuration de la route
Pour que Symfony sache quoi faire lorsqu'on appelle la page http://mon-site/user/login,
il faut modifier le fichier Resources/config/routing.yml
dans votre bundle.
Ajoutez-y les deux routes suivantes, pour se connecter et se déconnecter :
acme_my_user_login:
pattern: /user/login{redirectURI}
defaults: { _controller: AcmeMyBundle:UserConnection:login }
requirements:
redirectURI: ".*"
acme_my_user_logout:
pattern: /user/logout{redirectURI}
defaults: { _controller: AcmeMyBundle:UserConnection:logout }
requirements:
redirectURI: ".*"
Explications :
- Une route doit être nommée par un identifiant unique de votre choix (ex :
acme_my_user_login
).
C'est lui qui est appelé dans la fonction path()
côté template.
- Elle associe un pattern d'URL, à l'action d'un contrôleur.
- Pour la première route, si on appelle la page
/user/login<quelque chose>
, la méthode loginAction()
du contrôleur UserConnectionController
du bundle AcmeMyBundle
sera exécutée.
La méthode recevra quelque chose
en argument.
- Les éléments dans
requirements
permettent entre autres de spécifier la forme que doit avoir le paramètre.
Ici, n'importe qu'elle chaîne est autorisée.