Symfony
Symfony
Introduction
AYMEN SELLAOUTI
C’est quoi symfony ?
« Symfony is a set of PHP Components, a Web Application framework,
a Philosophy, and a Community — all working together in harmony. »
[site Officiel Symfony]
Ensemble de composants PHP
Framework pour les applications web
Basée sur des composants
Structuration du code
Maintenabilité
C’est quoi symfony ?
Une philosophie
Les bonnes pratiques
standardisation
Une très grande communauté
FrameWork : Cadre De Travail (Boite à outils)
Ensemble de composants servant à créer :
Fondation
Architecture
Pourquoi utiliser un Framework
Productivité : ensemble de composants déjà prêt à l’emploi
Communauté et documentation
Installation de Symfony (1)
Afin d’installer Symfony, vous devez disposer de Composer qui est un
PHP Package Manager. https://getcomposer.org/download/
Pré requis :
PHP 8.1 ou plus, ceci dépendra de la version Symfony.
Lancer la commande : composer create-project
symfony/website-skeleton nomProjet pour une version d’un projet
web qui contient les bibliothèques de bases dédiées à un projet web.
Lancer la commande : composer create-project
symfony/skeleton nomProjet pour une version orientée vers les
microservices ou les API.
Installation de Symfony (2)
Vous pouvez aussi utiliser le symfony client : https://symfony.com/download
Installer tout d’abord scoop (pour windows) via ce lien https://scoop.sh/
Lancer ensuite la commande scoop install symfony-cli
Vous n’avez qu’à utiliser donc la commande symfony new nomProjet pour
avoir votre projet.
Vous allez avoir deux choix, la version Webapp qui contient l’ensemble des
bibliothèques nécessaires pour la création d’une application ajouter l’option
--webapp.
La version minimaliste pour créer des api, des micro services ou une
application desktop.
Installation de Symfony (3)
symfony new my_project_directory --version="6.1.*" --webapp
symfony new my_project_directory --version="6.1.*“
composer create-project symfony/skeleton:"6.1.*" my_project_directory
composer create-project symfony/skeleton:"6.1.*" my_project_directory
Symfony Roadmap
Vous pouvez vérifier les différentes roadmap des versions symfony.
Choisissez les versions stables LTS (Long Term Support)
https://symfony.com/roadmap
Architecture d’un projet Symfony 2.8 et 3.*
•app/ : La configuration de l'application,
•src/ : Le code PHP du projet,
•vendor/: Les bibliothèques tierces,
•web/ : Le répertoire Web racine.
Symfony 3
Identifier les
différences entre
symfony2.8 et 3
Architecture d’un projet Symfony 4.*
Le contrôleur frontal (1)
public/index.php
Il joue le rôle de dispatcheur :
https://symfony.com/doc/4.2/create_framework/front_controller.html
Le contrôleur frontal (2)
Le contrôleur principal s'occupe de gérer les requêtes, mais cela signifie
plus que simplement déterminer une action à exécuter. En fait, il
exécute le code qui est commun à chaque action, soit les étapes
suivantes :
Définir les constantes.
Déterminer les chemins des bibliothèques Symfony.
Charger et initialiser les classes du cœur du framework.
Charger la configuration.
Symfony 6
Les contrôleurs
AYMEN SELLAOUTI
1
Introduction (1)
2
Introduction (2)
/ rootAction Réponse
R R
e e
q p
Contrôleur Noyau Symfony2
u /root1 Frontal
rootAction1 Réponse e
ê n
t s
Requête
e /root2 URI
Contrôleur e
rootAction2 Réponse
s s
Routeur
3
Introduction (3)
Fonction PHP (action)
Rôle : Répondre aux requêtes des clients.
Page HTML
Requête HTTP Réponse Document XML
Contrôleur Tableau JSON
Image
Redirection
…
4
Exemple d’un contrôleur
5
Exercice
Créer une classe FirstController
Créer une action first
Faite en sorte que lors de l’appel de la route /first cette action soit
exécuté et qu’elle affiche une page contenant ‘Hello forma’
Fonctions de base de la classe
AbstractController
Méthode fonctionnalité Valeur de retour
generateUrl(string $route, array() $parameters) Génère une URL à partir de la route String
forward(String Action, array () $parameters) Forward la requête vers un autre Response
contrôleur
Redirect(string $url, int $tatut) Redirige vers une url RedirectResponse
RedirectToRoute(string $route, array $parameters) Redirige vers une route Response
Render(string $view, array $parameters) Affiche une vue Response
Get(string $id) Retourne un service à travers son id object
createNotFoundException(String $messag) Retourne une NotFoundException NotFoundException
7
Génération automatique d’un contrôleur
Afin d’automatiquement générer un contrôleur via la console, vous
pouvez utiliser le maker de Symfony disponible depuis sa version 4.
8
Lien entre la route et le contrôleur
Création de la route
Voici un exemple de correspondance entre une route et le contrôleur
qui lui est associé :
Nous prenons l’exemple d’une route écrite en attributs, YAML et en
annotation.
#[Route('/personne', name: ‘personne')]
personne:
path: /personne
controller: App\Controller\PersonneController::index
/**
* @Route("/personne", name="personne")
*/
9
Lien entre la route et le contrôleur
Passage de paramétré : route vers contrôleur
Afin de récupérer les paramètres de la root dans le contrôleur nous
utilisons les noms des paramètres.
Exemple
10
Exercice
Dans la classe FirstController
Créer une méthode param qui prend en paramètre une variable nom à
travers la route.
Faite en sorte d’afficher Bonjour suivi du nom passé en paramètre.
Récupérer les paramètres de la requête (1)
Afin de récupérer l’objet Request dans le contrôleur, il suffit d’utiliser le
type-hint et le déclarer dans l’entête du contrôleur en question En
spécifiant qu’il s’agit d’un objet de type Request.
Exemple
use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $req)
{
…
}
12
Exercice
Dans la classe FirstController
Créer une action second
Faite en sorte d’y dumper (via la fonction dump) l’objet Request et
l’objet Response. Vérifier les informations encapsulées par ses deux
objets.
Récupérer les paramètres de la requête (2)
L’objet Request permet de récupérer l’ensemble des attributs passées
dans la requête
Méthode
Type de paramètres Méthode Symfony2 Exemple
traditionnelle
Variables d'URL $request->query $_GET $request->query->get(‘var')
Variables de $request->request $_POST $request->request->get(‘var')
formulaire
Variables de cookie $request->cookies $_COOKIE $request->cookies->get(‘var')
14
Récupérer les paramètres de la requête (3)
Exemple : pour l’url suivante
http://127.0.0.1/symfoRT4/web/index.php/test/bonjour/forma?groupe=1
Pour récupérer le groupe passé dans l’url (donc du Get) on devra récupérer le
request puis utiliser $request->query->get('tag')
15
Récupérer les paramètres de la requête (4)
La classe Request offre plusieurs informations concernant la requête
HTTP à travers un ensemble de méthodes
(https://symfony.com/doc/current/introduction/http_fundamentals.ht
ml#symfony-request-object)
getMethod() : retourne la méthode de ma requête
isMethod() : vérifie la méthode
getLocale : retourne la locale de la requête (langue)
isXmlHttpRequest : retourne vrai si la requête est de type
XmlHttpRequest
…
16
Réponse aux requêtes
Renvoi (1)
Le rôle principale du contrôleur est de répondre à la requête du client en envoyant
une réponse.
Vous pouvez créer un objet Response et y injecter votre contenu et le retourner.
Sinon, le traitement se fait à travers un Helper qui utilise le service templating qui
se charge de créer et d’irriguer un objet de type Response.
Rôle : créer la réponse et la retourner
Méthode :
En utilisant les helpers :
$this->render (‘l’url’, ‘les paramétres à transferer’);
17
Réponse aux requêtes
Renvoi (2)
Exemple :
public function index ($section,Request $req)
{
$groupe = $request->query->get(‘groupe');
return $this->render(‘default/index.html.twig’, array(’section’=>$section,
‘goupe’=>$groupe));
}
18
Exercice
Créer l’action (contrôleur) cv
Préparer des variables permettant de créer un mini Portfolio. Le contenu de ces variables devra vous
permettre d’afficher vos nom et prénom, votre âge et votre Section.
Ensuite, utiliser la méthode render afin d’invoquer votre page TWIG en lui passant les variables que
vous venez de préparer.
Sachant que pour afficher une variable dans TWIG il suffit de l’entourer de {{ nomVariable }}, créer
une page TWIG « cv.html.twig » dans un dossier « Premier» que vous créerez dans le dossier
« templates ».
Cette page devra afficher les données transmises par votre contrôleur
19
Réponse aux requêtes
Redirection
Rôle : Redirection vers une deuxième page ( en cas d’erreur
ou de paramètres erronées ou de user non identifié par
exemple)
Redirection vers une url.
return this->redirect($url);
20
Réponse aux requêtes
Forwarding
Rôle : Forwarder vers une action
Méthode :
$response = $this->forward('App\Controller\NomController::NomAction',
array('name' => $name));
21
Gestion des sessions
Une des fonctionnalités de base d’un contrôleur est la
manipulation des sessions.
Un objet session est fournit avec l’objet Request.
La méthode getSession() permet de récupérer la session.
Il est préférable d’utiliser le type-hint via L’interface
SessionInterface :
public function index (SessionInterface $session)
22
Gestion des sessions
L’objet Session fournit deux méthodes : get( ) pour récupérer une
variable de session et set( ) pour la modifier ou l’ajouter.
get prend en paramètre la variable de session.
set prend en entrée deux paramètres la clef et la valeur.
Dans la TWIG on récupère les paramètres de la session avec la
méthode
app.session.get(‘nomParamétre')
23
Gestion des sessions : les méthodes
all() Retourne tous les attributs de la session dans un tableau de la
forme clef valeur
has() Permet de vérifier si un attribut existe dans la session. Retourne
Vrai s’il existe Faux sinon
replace() Définit plusieurs attributs à la fois: prend un tableau et
définit chaque paire clé => valeur
remove() Efface un attribut d’une clé donnée.
clear() Efface tous les attributs.
24
Gestion des sessions : les FlashMessages
Les variables de sessions qui ne dure que le temps d’une seule page
sont appelées message Flash.
Utilisées généralement pour afficher un message après un traitement
particulier ( Ajout d’un enregistrement, connexion, …).
La méthode getFlashBag() permet de récupérer l’objet FlashBag à
partir de l’objet session.
La méthode add de cet objet permet d’ajouter une variable à cet objet.
Vous pouvez utilisez un helper via la méthode addFlash.
25
Gestion des sessions : les FlashMessages
Pour récupérer le Flash message de la TWIG on utilise
app.session.flashbag.get(‘nomParamétre‘).
Vous pouvez aussi utiliser la méthode flashes de la variable globale
app qui contient le tableau des flashBags messages.
26
Exercice
Créer un contrôleur appelé ToDoController
Créer une première action (indexAction) qui permet d’initialiser Astuce : Afin d’afficher les éléments et les
un tableau associatif de clés d’un tableau associatif dans la twig on
todos et qui le met dans la session puis appelle la page peut utiliser la syntaxe suivante qui sera
‘listeToDo.html.twig’. Lors de l’appel de ce contrôleur, il faudra explicité dans le chapitre consacré aux TWIG.
{% for cle,element in tableau %}
vérifier si la liste des todo existe déjà dans la session. Si la liste existe
{{ cle }} {{ element }}
il ne faut pas la réinitialiser. {% endfor %}
Exemple $todos = array(
'achat'=>'acheter clé usb',
'cours'=>'Finaliser mon cours',
'correction'=>'corriger mes examens'
);
27
Exercice
Quelques Astuces
Ajouter bootstrap pour avoir les classes Alert ou un peu de css pour
colorer le background de vos DIV ou paragraphe.
Utiliser la fonction unset de php qui permet de supprimer un élément
du tableau partir de sa clé.
Pour ajouter un élément dans un tableau associatif il suffit d’utiliser la
section suivante : $monTab[‘identifiant’]=$var ;
28
Exercice
Créer une action addToDoAction qui permet d’ajouter un ToDo ou
de le mettre à jour. Cette action devra afficher la liste mise à jour. Si
la liste de ToDo n’est pas encore initialisée, un message d’erreur
sera affiché.
29
Exercice
Si le ToDo est ajouté avec succès, un message de succès sera
affiché. Si le ToDo est mis à jour il faut le mentionner.
30
Exercice
Créer une action deleteToDo qui permet de supprimer un ToDo à
partir de son indice dans le tableau. Cette action devra afficher la
liste mise à jour. Si le toDo à supprimer n’existe pas, un message
d’erreur est affiché.
Si la suppression est effectuée avec succès un message est
affiché.
31
Exercice
Créer une action resetToDo qui permet de vider la session et de la
remettre à son état initial. Prenez en considération le cas où la liste
n’est pas encore initialisée
32
{# templates/base.html.twig #}
{# read and display just one flash message type #}
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
34
Symfony 6
Routing
AYMEN SELLAOUTI
Introduction
Système permettant de
Routing (http://www.lafermeduweb.net/)
Format de gestion du routing
Les fichiers de routing peuvent être de quatre formats différents :
YAML
XML
PHP
Annotations
Attributs
Squellete d’une route
Jusqu’à la version 3, Sensio recommande l’utilisation du format Yaml
au sein des applications. Les bundles développés sont partagés entre
deux formats : le XML e le Yaml.
Sensio a décidé de recommander Yaml parece qu’il est « user-friendly ».
A partir de la version 3.4, la documentation s’est focalisé
essentiellement sur les annotation et la recommande. Nous allons donc
voir ces deux formats.
Dans la version 8 de PHP, une nouvelle syntaxe plus lisible et plus
fonctionnelle a été introduite : les attributs.
Squelette d’une route en utilisant les
Annotations et les attributs
/**
*
* @Route("/blog", name="blog_list") #[Route('/blog', name: 'blog_list')]
*/ public function list(): Response
{
public function list()
// ...
{ }
// ...
}
Squelette d’une route en utilisant YAML
# config/routes.yaml
blog_list:
# Matches /blog exactly
path: /blog
controller: App\Controller\BlogController::list
https://symfony.com/doc/current/routing.html
Squelette d’une route en utilisant XML
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
https://symfony.com/schema/routing/routing-1.0.xsd">
https://symfony.com/doc/current/routing.html
Squelette d’une route en utilisant PHP
<?php
// config/routes.php
use App\Controller\BlogController;
use
Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
https://symfony.com/doc/current/routing.html
Organisation des routes YAML
Lorsque vous utilisez YAML, il est préférable de ne pas centraliser
l’ensemble de vos routes dans un même fichier. Ceci peut nuire à la
lisibilité de vos routes.
Décomposer vos routes en des fichiers logiques. Exemple si vous avez
une partie Back et une partie front, ayez deux fichiers back.yaml et
front.yaml.
Garder le fichier principal de vos routes pour appeler ces fichiers la.
Organisation des routes YAML
Afin d’identifier un fichier de ressources « route» utiliser la clé
ressource afin d’informer le chemin de la ressource et la clé type afin
de spécifier le type de votre ressource (dans notre cas c’est directory).
Les préfixes
Dans certains cas vous avez besoins d’avoir des endpoints
particuliers pour un ensemble de fonctionnalités. Par exemple
lorsque vous aller réaliser une partie administration vous aurez
généralement des routes qui commencent par /admin.
app_personne:
resource: 'personne'
type: directory
prefix: /personne
https://symfony.com/doc/4.2/routing/external_resources.html
Préfixe annotation
Une annotation Route sur une classe Contrôleur définit un préfixe
sur toutes les routes des actions de ce contrôleur
/**
* @Route("/personne")
*/
class PersonneController extends AbstractController
{
}
#[Route('/first']
class FirstController extends AbstractController
Paramétrage de la route : Yaml
Nous pouvons ajouter autant de paramètre dans la route
Le séparateur est ‘/’
Exemple
front_article:
path: /article/{year}/{langue}/{slug}/{format}
controller: App\Controller\BlogController::add
Ici l’url doit contenir l’année de l’article {year}, la langue de
l’article{langue} les mots clefs {slug} ainsi que le format {fomrat}
Paramétrage de la route : Annotation
/**
* @Route("/hello/{name}", name=" front_homepage")
*/
public function test ($name){}
/**
* @Route("/article/{year}/{locale}/{slug}/{format}",
name="front_article")
*/
public function showArticle($year,$locale,$slug,$format){}
Paramétrage de la route : Attributs
#[Route('/second/{name}', name: 'app_second')]
public function index($name): Response
{
return $this->render('second/index.html.twig', [
'myName' => $name,
]);
}
Paramétrage de la route : valeurs par
défaut Annotations
En utilisant les annotations, nous ajoutons un champ defaults qui
contiendra les valeurs par défaut.
/**
* @Route(
* "/article/{year}/{locale}/{slug}/{format}",
* name="front_article",
* defaults={"format":"html", "slug":"Symfony"}
* )
*/
public function showArticleAction($year,$_locale,$slug,$_format){
dump($year,$_locale,$slug,$_format);
die();
} Important : Seul les paramètres optionnels terminant la route pourront être absents de l’URL
}
Paramétrage de la route : Requirements
Attributs
En utilisant les annotations, nous ajoutons un champ requirements
qui contiendra les différentes contraintes.
#[Route(
'/second/{name}/{age}',
name: 'app_second',
requirements: ['age'=> '\d+']
)]
public function index($name, $age): Response
{
return $this->render('second/index.html.twig', [
'myName' => $name,
'myAge'
]);
}
Paramétrage de la route : Requirements
Attributs
En utilisant les annotations, nous ajoutons un champ requirements
qui contiendra les différentes contraintes.
#[Route(
'/second/{name}/{age<\d+>}',
name: 'app_second',
)]
public function index($name, $age): Response
{
return $this->render('second/index.html.twig', [
'myName' => $name,
'myAge‘=> $age
]);
}
Requirements autres exemples
\d équivalente à \d{1}
\d+ ensemble d’entiers
Ordre de traitement des routes (1)
Le traitement des routes se fait de la première route vers la dernière.
Attention à l’ordre d’écriture de vos routes.
front: Comment accéder au path front_pages ? Quel est le problème avec ces 2 routes
path: /front/{page}
controller: App\Controller\BlogController::front
front_pages:
path: /front/{Keys}
controller: App\Controller\BlogController::show
Exercice
Reprenez les deux routes précédentes.
Afficher une variable Variable : {{ MaVariable }} Pseudo : <?php echo $MaVariable; ?>
Exemple
{% set sum = 0 %}
Structure conditionnelle
if
Syntaxe :
{% if cnd %}
Block de traitement
{%endif%}
Exemple
{% if employee.salaire < 250 %}
ce salaire est inférieur au smig
{%endif%}
Structure conditionnelle
IF else elseif Exemple
Syntaxe :
{% if cnd %} {% if maison.tempertature <0%}
block traitement Très Froid
{% elseif cnd2 %} {% elseif maison.tempertature <10 %}
block traitement
Froid
{% else %}
{% else %}
block traitement
Bonne température
{% endif %}
{% endif %}
Tests
is defined l’équivalent de isset en php
Rôle vérifie l’existence d’une variable
Exemple:{% if MaVariable is defined %} J’existe {% endif%}
even odd
Rôle vérifie si la variable est pair Rôle vérifie si la variable est impair
Exemple : {% if MaVariable is even %}Pair{% endif%} Exemple : {% if MaVariable is odd%}Impair{% endif%}
Structure itérative
{% for valeur in valeurs %} Exemple
block à répéter La formation de l’équipe A est : <br>
{% else %} <ol>
Traitements à faire si il n’y {% for joueur in joueurs %}
a aucune valeur <li> {{joueur.nom}} </li>
{% endfor %} {% else %}
La liste n’a pas encore été renseignée
{% endfor %}
</ol>
Structure itérative
La boucle for définit une variable loop ayant les attributs suivants :
Variable Description
{{ loop.revindex }} Le nombre d'itérations restantes avant la fin de la boucle (en finissant par 1).
{{ loop.revindex0 }} Le nombre d'itérations restantes avant la fin de la boucle (en finissant par 0).
Surdéfinition de blocs
Template
fils 3.1
Héritage 2 – Exemple de Template père
Exemple de Template de base
Héritage 3- Syntaxe
Afin d’hériter d’un Template père il faut étendre ce dernier avec la
syntaxe suivante :
{% extends 'TemplateDeBase' %}
Exemple :
{% extends 'base.html.twig' %}
Si le fils ne surcharge aucun des blocs et n’ajoute rien on aura le même
affichage que pour la base
Héritage 4- Le Bloc
L’élément de base de l’héritage est le bloc
Un bloc est définit comme suit : {% block nomBloc%} contenu du
bloc{%endblock%}
Pour changer le contenu de la page il faut sur-définir le bloc cible
En héritant d’une page et si vous écrivez du code à l’extérieur des
blocs vous aurez le message suivant qui est très explicite.
Héritage 5- Récupérer le contenu d’un
bloc parent
Pour récupérer le contenu d’un bloc père il suffit d’utiliser la méthode
parent()
{% block body %}
{{ dump(app) }}
{% endblock %}
Symfony 6
L’ORM DOCTRINE
AYMEN SELLAOUTI
1
Introduction (1)
2
Introduction (2)
ORM : Object Relation Mapper
Couche d’abstraction
Gérer la persistance des données
Mapper les tables de la base de données relationnelle avec
des objets
Crée l'illusion d'une base de données orientée objet à
partir d'une base de données relationnelle en définissant des
correspondances entre cette base de données et les objets
du langage utilisé.
Propose des méthodes prédéfinies
3
Introduction (3)
4
Fonctionnement de Doctrine
Doctrine utilise deux design pattern objet :
Data Mapper
Unit of Work
5
Data Mapper
C’est la couche entre les objets (entités) et les tables de la base de données.
Dans le cas de PHP elle synchronise les données stockées en base de données avec vos objets PHP.
Elle se charge d’insérer et de mettre à jour les données de la base en se basant sur le contenu des
propriété de votre objet.
Elle peut aussi supprimer des enregistrement de la base de données.
Elle peut aussi hydrater vos objets en utilisant les informations contenues dans la base de données.
Ceci permet d’avoir une abstraction complète de la base de données vu que les objets sont
indépendants du système de stockage. C’est le Data Mapper qui se charge de ca.
Doctrine implémente ce design pattern via l’objet EntityManager.
6
Unit of Work
Afin d’éviter une multitude de petite requêtes envoyé à votre base de données, et
de garder un historique des requetés effectuées au niveau de votre base de données,
Doctrine utilise le design pattern Unit of work.
Pour une raison de performance et d'intégrité, La synchronisation effectuée par
l'Entity Manager ne se fait pas pour chaque changement avec la base de données.
Le design pattern Unit of Work permet de gérer l'état des différents objets
hydratés par l'Entity Manager.
Une transaction est ouverte regroupant l’ensemble des opérations. Le commit de
cette transaction déclenchera l’exécution de l’ensemble de ses requêtes.
En cas d’échec l’ensemble des requêtes et annulées.
7
Les entités (1)
Objet PHP
Les entités représente les objets PHP équivalentes à une table de la
base de données.
Une entité est généralement composée par les attributs de la tables
ainsi que leurs getters et setters
Manipulable par l’ORM
8
Les entités (2)
Exemple
9
Configuration des entités
Configuration Externe : YAML, XML, PHP
Configuration Interne : annotations, attributs (php8)
Choix de la configuration ?
Deux Visions :
Pro-Externe
Séparation complète des fonctionnalités spécialement lorsque l’entité est
conséquente
Pro-Interne
Plus facile et agréable de chercher dans un seul fichier l’ensemble des information,
plus de visibilité
10
Mapping : Annotation et attributs des
entités (1)
Rôle : Faire le lien entre les entités et les tables de la base de données
Lien à travers les métadonnées
Remarque : Un seul format par bundle (impossibilité de mélanger)
Syntaxe :
/**
* les différentes annotations
*/
Remarque : Afin d’utiliser les annotations il faut ajouter :
use Doctrine\ORM\Mapping as ORM;
11
Mapping : Annotation et attributs des entités (2)
Entity
Permet de définir un objet comme une entité
Applicable sur une classe
Placée avant la définition de la classe en PHP
Syntaxe : @ORM\Entity #[ORM\Entity]
Paramètres :
repositoryClass (facultatif). Permet de préciser le namespace complet du repository
qui gère cette entité.
Exemple :
@ORM\Entity(repositoryClass="Rt4\AsBundle\Entity\animalRepository")
#[ORM\Entity(repositoryClass: PersonneRepository::class)]
12
Mapping : Annotation et attributs des entités (3)
Table
Permet de spécifier le nom de la table dans la base de données à associer à
l'entité
Applicable sur une classe et placée avant la définition de la classe en PHP
Facultative sans cette annotation le nom de la table sera automatiquement le
nom de l'entité
Généralement utilisable pour ajouter des préfixes ou pour forcer la première
lettre de la table en minuscule /**
*
#[ORM\Table(‘table’)]
Syntaxe : @ORM\Table() * @ORM\Table(‘animal’)
*
Exemple : @ORM\Entity(repositoryClass="Rt4\AsBundle\Entity\animalRe
pository")
*/
13
Mapping : Annotation et attributs des entités (4)
Column
Permet de définir les caractéristiques de la colonne concernée
Applicable sur un attribut de classe juste avant la définition PHP de l'attribut
concerné.
Syntaxe : @ORM\Column()
Exemple :
/**
#[ORM\Column(type: Types::STRING,length: 255)]
*
* @ORM\Column(param1="valParam1" ,param2="valParam2")
*/
14
Mapping : Annotation et attributs des entités (5)
Les paramètres de Column
Paramètre Valeur par défaut Utilisation
type string Définit le type de colonne comme nous venons de
le voir.
name Nom de l'attribut Définit le nom de la colonne dans la table. Par
défaut, le nom de la colonne est le nom de l'attribut
de l'objet
length 255 Définit la longueur de la colonne (pour les strings).
unique false Définit la colonne comme unique (Exemple :
email).
nullable false Permet à la colonne de contenir des NULL.
precision 0 Définit la précision d'un nombre à virgule(decimal)
scale 0 le nombre de chiffres après la virgule (decimal)
15
Mapping : Annotation et attributs des entités (6)
Les types de Column
Type Doctrine Type SQL Type PHP Utilisation
string VARCHAR string Toutes les chaînes de caractères jusqu'à
255 caractères.
integer INT integer Tous les nombres jusqu'à 2 147 483 647.
smallint SMALLINT integer Tous les nombres jusqu'à 32 767.
bigint BIGINT string Tous les nombres jusqu'à 9 223 372 036
854 775 807.
16
Mapping : Annotation et attributs des entités (7)
Les types de Column
Type Doctrine Type SQL Type PHP Utilisation
date ou datetime DATETIME objet DateTime Toutes les dates et heures.
time TIME objet DateTime- Toutes les heures.
text CLOB string Les chaînes de caractères de plus de 255 caractères.
object CLOB Type de l'objet stocké Stocke un objet PHP en utilisant serialize/unserialize.
array CLOB array Stocke un tableau PHP en utilisant serialize/unserialize.
float FLOAT double Tous les nombres à virgule.
Attention, fonctionne uniquement sur les serveurs dont
la locale utilise un point comme séparateur.
17
Mapping : Annotation et attributs des entités (8)
Conventions de Nommage
Même s’il reste facultatif, le champs « name » doit être modifié afin de
respecter les conventions de nommage qui diffèrent entre ceux de la
base de données et ceux de la programmation OO.
Les noms de classes sont écrites en « Pascal Case » TheEntity.
Les attributs de classes sont écrites en « camel Case » oneAttribute
Les noms des tables et des colonnes en SQL sont écrites en
minuscules, les mots sont séparés par « _ » one_table, one_column.
18
Gestion de la base de données (1)
Configuration de l’application
Afin de configurer la base de données de l’application il faut renseigner les champs
dans le fichier .env qui est renseigné dans le fichier config/packages/doctrine.yml
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
DATABASE_URL="mysql://root:@127.0.0.1:3306/sf1test?serverVersion=10.4.24-MariaDB&charset=utf8mb4"
#DATABASE_URL="postgresql://username:[email protected]:5432/app?serverVersion=10.4.24-MariaDB&charset=utf8"
Gestion de la base de données (2)
Création de base de données
Afin de créer la base de données du projet 2 méthodes sont utilisées :
Manuelle en utilisant le SGBD (le nom de la BD doit être le même
que celui mentionné dans le fichier doctrine.yml)
En utilisant la ligne de command avec la command suivante :
php bin/console doctrine:database:create
symfony console doctrine:database:create
Une base de données avec les propriétés mentionnées dans .env sera
automatiquement générée
21
Gestion de la base de données (3)
Génération des entités
Deux méthodes pour générer les entités :
Méthode manuelle (non recommandée)
Créer la classe
Ajouter le mapping
Ajouter les getters et les setters (manuellement ou en utilisant la commande
suivante :
php bin/console make:entity --regenerate App
(elle crée les getters et les setters de toutes les entités)
22
Gestion de la base de données (3)
Génération des entités
Deux méthodes pour générer les entités :
Méthode en utilisant les commandes
Il suffit de lancer la commande suivante :
php bin/console make:entity
symfony console make:entity
Ajouter les attributs ainsi que les paramètres qui vont avec
Une fois terminé, Doctrine génère l’entité avec toutes les métadonnées de
mapping
23
Gestion de la base de données
Les migrations
Les migrations sont une nouvelle façon utilisée par Symfony 4 afin de
gérer les mises à jours et évolutions de votre base de données.
24
Gestion de la base de données
Les migrations : les commandes
Pour connaitre l’état de vos fichiers de migrations, vous pouvez utiliser
la commande
php bin/console doctrine:migration:status
25
Gestion de la base de données
Les migrations : les commandes
Créer un fichier de migration
Pour créer un fichier de migration, utilisez la commande :
symfony console doctrine:migration:generate.
Ceci vous créera un fichier de migration vide de tout traitement.
Si vous avez besoin d’écrire votre propre code de migration vous
pouvez le faire.
26
Les migrations
Créer un fichier de migration pour adapter
votre base de données à vos entités
Pour créer un fichier de migration, utilisez la commande :
symfony console doctrine:migration:diff
Vous pouvez aussi utiliser
php bin/console make:migration
Cette commande fait appel à la commande précédente.
27
<?php
//….
Exemple
29
Résumé
:diff [diff] Génères une migration en comparant la base de données avec
les informations de mapping.
:execute [execute] Exécute une migration manuellement.
:generate [generate] Crées une classe de Migration.
:migrate [migrate] Effectues une migration vers le fichier de migration le
plus récent ou celui spécifié.
:status [status] Affiche le status des migrations.
:version [version] Ajoute et supprime manuellement des versions à partir
de la version en en base.
30
Exercice
Créer votre base de données à travers la ligne de commande.
Générer une Entité Personne.
Cette entité contient un attribut id, un attribut nom, prenom, age, cin
et path.
Doctrine
Doctrine
Le service Doctrine
Rôle : permet la gestion des données dans la BD : persistance des données et consultation des données.
33
Le service EntityManager
Rôle : L’interface ORM proposée par doctrine offrant des méthodes
prédéfini pour persister dans la base ou pour mettre à jour ou supprimer
une entité.
Méthode :
$EntityManager = $this->get(‘doctrineorm.entity_manager’)
$doctrine->getManager();
L’injecter en tant que service (à voir dans la partie service)
34
Le service EntityManager
Insertion des données
Enregistrement des données
Etant un ORM, Doctrine traite les objets PHP
Pour enregistrer des données dans la BD il faut préparer les objets contenant ces
données la
La méthode persist() de l’enityManager permet d’associer les objets à persister avec
Doctrine
Afin d’exécuter les requêtes sur la BD (enregistrer les données dans la base) il faut
utiliser la méthode flush()
L’utilisation de flush permet de profiter de la force de Doctrine qui utilise les
Transactions
La persistance agit de la même façon avec l’ajout (insert) ou la mise à jour (update)
35
Exercice
Créer un contrôleur PersonneController
Créer une action qui permet l’ajout d’une Personne au niveau de la base de
données.
Les données seront introduites via la route.
Une fois la personne ajoutée, une page contenant ses informations sera affichée.
Faite de même pour la mise à jour, faite en sorte que cette action prenne l’id de la
personne à modifier et les nouvelles valeurs.
Le service EntityManager
Suppression d’une entité
Suppression d’une entité
La méthode remove() permet de supprimer une entité
37
Exercice
Créer une action qui permet la suppression d’une personne en utilisant
son id.
Si la personne n’existe pas un message d’erreur est affiché
Une petite parenthèse : Fixtures
Les fixtures sont utilisées pour charger des données « fake » au sein de
votre base de données pour tester les fonctionnalités que vous avez
développé.
Installer le bundle responsable des Fixtures.
composer require --dev orm-fixtures
Créer une classe VotreEntitéFixture à l’aide de la commande :
symfony console make:fixtures
https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html 39
Une petite parenthèse : Fixtures
Implémenter la méthode load avec le fonctionnement que vous voulez
pour charger vos données.
Lancer vos fixtures : php bin/console doctrine:fixtures:load, cette
commande effacera le contenu de votre Base de données.
Si vous voulez garder la base ajouter l’option –append
php bin/console doctrine:fixtures:load --append
https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html 40
Fixture exemple
<?php
namespace App\DataFixtures;
use App\Entity\Personne;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
$manager->flush();
}
} 41
Exercice
Créer un fixture permettant d’ajouter quelques personnes dans la base
de données.
https://fakerphp.github.io/
43
Le Repository
Quelque méthodes offertes par le repository :
$repository->findAll(); // récupère tous les entités (enregistrements) relatifs
à l’entité associé au repository
$repository->find($id);// requête sur la clé primaire
$repository->findBy();//retourne un ensemble d’entités avec un filtrage sur
plusieurs critères (nbre donné)
$repository->findOneBy();//même principe que findBy mais une seule
entité
$repository->findByNomPropriété();
$repository->findOneByNomPropriété();
44
Le Repository
findAll
findAll()
Rôle : retourne l’ensemble des entités qui correspondent à l’entité associé au
repositorie. Le format du retour est un Array
Exemple :
//On récupère le repositorie de l’entity manager correspondant à l’entité Etudiant
$repository = $doctrine->getRepository(ClassName::class) ;
//On récupère la liste des étudiants
$listAdverts = $repository->findAll();
Généralement le tableau obtenu est passé à la vue (TWIG) et est affiché en
utilisant un foreach
45
Exercice
Créer une page list.html.twig
Faite en sorte que cette page affiche la liste des personnes de votre base de
données.
Le Repository
find
find($id)
Rôle : retourne l’entité qui correspond à la clé primaire passé en
argument. Généralement cette clé est l’id.
Exemple :
//On récupère le repository de l’entity manager correspondant à
l’entité Etudiant
$repo = $doctrine->getRepository(Etudiant::class);
//on lance la requéte sur l’étudiant d’id 2
$etudiant = $repository->find(2);
47
Exercice
Créer une page profil.html.twig
Faite en sorte que cette page affiche le profil d’une personne selon son
id.
Le Repository
findBy
findBy()
Rôle : retourne l’ensemble des entités qui correspondent à l’entité associé au
repositorie comme findAll sauf qu’elle permet d’effectuer un filtrage sur un
ensemble de critère passés dans un Array. Elle offre la possibilité de trier les entités
sélectionnées et facilite la pagination en offrant un nombre de valuer de retour.
Synatxe: $repository->findBy( array $criteria, array $orderBy = null, $limit = null,
$offset = null);
Exemple : $repository = $doctrine->getRepository(Etudiant::class);
$listeEtudiants = $repository->findBy(array('section' => 'RT4','nom' =>
'Mohamed'), array('date' => 'desc'),10, 0);
49
Exercice
Créer une méthode qui permet d’afficher la liste des personnes d’un
nom donnée ordonnée par prénom.
Le nombre maximum de résultat à afficher est de 5.
Le Repository
findOneBy
findOneBy()
Rôle : Même principe ue FindBy mais en retournant une seule entité ce
qui élimine automatiquement les paramètres d’ordre de limite et d’offset
Exemple :
$repository = $doctrine->getRepository(Etudiant::class);
$Etud = $repository->findOneBy(
array('section' => 'RT4','nom' => 'Mohamed')
);
51
Le Repository
findByPropriété
findByPropriété()
Rôle : En remplaçant le mot Propriété par le nom d’une des propriété
de l’entité, la fonction va faire le même rôle que findBy mais avec un
seul critère qui est le nom de la propriété et sans les options.
Exemple :
$repository = $doctrine->getRepository(Etudiant::class);
$listeEtudiants = $repository->findByNom(‘Aymen’);
52
Le Repository
findOneByPropriété
findOneByPropriété()
Rôle : En remplaçant le mot Propriété par le nom d’une des propriété
de l’entité, la fonction va faire le même rôle que findOneBy mais avec
un seul critère.
Exemple :
$repository = $doctrine->getRepository(Etudiant::class);
$listeEtudiants = $repository->findOneByNom(‘Aymen’);
53
Le Repository
Création de requêtes
Les requêtes de doctrine sont écrites en utilisant le langage de doctrine
le Doctrine Query Lanquage DQL ou en utilisant un Objet créateur de
requêtes le CreateQueryBuilder
createQuery : Méthode de l’Entity Manager
CreateQueryBuilder : Méthode du repositorie
DQL
Classes + Propriétés
~ SQL
Tables + colonnes
https://symfony.com/doc/current/doctrine.html#querying-for-objects-the-repository 54
Le Repository
CreateQuery et DQL
Le DQL peut être défini comme une adaptation du SQL adapté à
l’orienté objet et donc à DOCTRINE
La requête est défini sous forme d’une chaine de caractère
Afin de créer une requête DQL il faut utiliser la méthode
createQuery() de l’EntityManager
La méthode setParameter(‘label’,’valeur’) permet de définir un
paramètre de la requête
55
Le Repository
CreateQuery et DQL
Pour définir plusieurs paramètres ou bien utiliser setParameter
plusieurs fois ou bien la méthode setParameters(array(‘label1’,’valeur1’,
‘label2’,’valeur2’),.. ‘labelN’,’valeurN’))
Une fois la requête créée la méthode getResult() permet de récupérer
un tableau de résultat
Le langage DQL est explicité dans le lien suivant :
http://docs.doctrine-project.org/projects/doctrine-
orm/en/latest/reference/dql-doctrine-query-language.html
56
Le Repository : Création de requêtes
createQuery
public function findPersonneByIntervalAge2($min, $max)
{
$query = $this->_em->createQuery(
'SELECT p
FROM App\Entity\Personne p
WHERE p.age >= :ageMin And p.age <= :ageMax
ORDER BY p.age
ASC'
)
->setParameter('ageMin', $min)
->setParameter('ageMax', $max)
;
return $query->execute();
}
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/dql-doctrine-query-language.html 57
Le Repository
QueryBuilder
Constructeur de requête DOCTRINE Alternative au DQL
Accessible via le Repositorie
Le résultat fourni par la méthode getQuery du QueryBuiler permet de
générer la requête en DQL
De même que le createQuery, une fois la requête créée la méthode
getResult() permet de récupérer un tableau de résultat.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html 58
Le Repository
QueryBuilder
Afin de récupérer le QueryBuilder dans notre Repository, on utilise la méthode
createQueryBuilder.
Cette méthode récupère en paramètre l’allias de l’entité cible et offre la requête
« select from » de l’entité en question.
Généralement l’allias est la première lettre en minuscule du nom de l’entité
Si aucun paramètre n’est passé a createQueryBuilder alors on aura une requête vide et
il faudra tout construire.
$qb=$this->_em->createQueryBuilder()
->select('t')
->from($this->_entityName,'alias');
$qb=$this->createQueryBuilder(‘t’)
59
Le Repository
QueryBuilder : Méthodes
from(‘entityName’,’entityAlias’)
from($this->_entityName,’t’)
where(‘condition’) permet d’ajouter le where dans la
requête
where(‘t.destination= :dest‘)
setParameter(‘nomParam’,param) permet d’ajouter la
définition d’un des paramètres définis dans le where
setParameter(‘dest’,$dest)
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html 60
Le Repository
QueryBuilder : Méthodes
andWhere(‘condition’) permet d’ajouter d’autres conditions
andWhere(‘t.statut = :status)
orderBy(‘nomChamp’,’ordre’) permet d’ajouter un orderBy et
prend en paramètre le champ à ordonner et l’ordre DESC ou
ASC.
orderBy(‘t.dateTransfert’,’DESC’)
setParameters(array(1=>’param1’,2=>’param2’))
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html 61
Le Repository
QueryBuilder : Méthodes
orWhere
groupBy
having
andHaving
orHaving
leftJoin
rightJoin
Join
innerJoin
…
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html 62
Le Repository
QueryBuilder : Méthodes
getQuery() : retourne la requête dql
getResult() : retourne un tableau d’objets contenant le résultat
getOneOrNullResult() : retourne le premier résultat ou Null
getSingleScalarResult() : retourne un résultat sus format scalaire.
Imaginer le use case ou vous voulez récupérer le COUNT ou la SUM
d’un de vos objets.
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/query-builder.html 63
Requêter des attributs spécifiques
Afin de sélectionner des propriétés particulières utiliser la méthode
select du queryBuilder
Syntaxe :
->select('SUM(u.age) as sumAge, AVG(u.age) as
avgUserAge')
64
Réutilisation de la logique de vos requêtes
Imaginer les uses cases suivants :
Vous voulez sélectionner les formations d’un topic donné.
Vous voulez sélectionner les formations d’un topic donné dont le nombre
d’inscrits est inférieur à un nombre données.
Vous voulez sélectionner les formations d’un topic donné dont le nombre
d’inscrits est supérieur à un nombre données.
Vous voulez sélectionner les formations d’un topic donné entre deux dates …
On remarque ici une redondance dans ces différentes requêtes.
L’idée est donc d’isoler ce traitements redondant dans une méthode et de la
réutiliser.
65
/**
* @param $min
* @param $max
* @return mixed
*/
public function getPersonneByAge($min, $max) {
$qb = $this->createQueryBuilder('p');
$qb = $this->findByAge($qb, $min, $max);
return $qb->getQuery()->getResult();
}
/**
* @param QueryBuilder $qb
* @param $min
* @param $max
* @return QueryBuilder
*/
private function findByAge(QueryBuilder $qb, $min, $max) {
if($min) {
$qb->andWhere('p.age > :minAge')
->setParameter('minAge', $min);
}
if($max) {
$qb->andWhere('p.age < :maxAge')
->setParameter('maxAge', $max);
}
return $qb;
}
66
public function getCommunesByZipCode($zipCode = null, $like = null,
$type = null) {
$qb = $this->createQueryBuilder('l')
->select('l.id, l.name, l.zipCode')
->where('1=1');
if ($zipCode) {
$qb = $qb->andWhere('l.zipCode = :zipCode ')
->setParameter('zipCode', $zipCode);
}
if ($like) {
$qb = $qb->andWhere('l.name like :like ')
->setParameter('like', "%$like%");
}
return $qb->getQuery()->getArrayResult();
}
Gestion des relations entre les entités (1)
Les types de relation
Les entités de la BD présentent des relations d’association :
68
Gestion des relations entre les entités (2)
Relation unidirectionnelle et bidirectionnelle
La notion de navigabilité de UML est la source de la notion de relation
unidirectionnelle ou bidirectionnelle
Une relation est dite navigable dans les deux sens si les deux entité doivent avoir
une trace de la relation.
Exemple : Supposons que nous avons les deux classes CandidatPresidentielle et
Electeur.
L’électeur doit savoir à qui il a voté donc il doit sauvegarder cette information par
contre le candidat pour cause d’anonymat de vote ne doit pas connaitre les
personnes qui ont voté pour lui.
On aura donc un attribut Candidat dans la table Electeur mais pas de collection ou
tableau nommé électeur dans la table CandidatPresidentielle. Ici on a une relation
unidirectionnelle.
69
Création de la relation
Vous pouvez créer votre relation via la console.
Créer un attribut et mettez y comme type « relation ».
Ceci va générer un attribut annoté avec la relation et les informations
minimalistes nécessaires pour sa création.
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Hobbies",inversedBy="personnes")
*/
private $hobbies;
70
Gestion des relations entre les entités (3)
OneToOne unidirectionnelle
Relation unidirectionnelle //**Entity **//
Class Etudiant
puisque Media ne référence pas {
Etudiant // …
/**
* @ORM\OneToOne(targetEntity=
targetEntity="App\Entity\Media"
)
*/
private $media;
// …
}
//**Entity **//
Class Etudiant
{
// …
}
71
Gestion des relations entre les entités (4)
OneToOne Bidirectionnelle
Si nous voulons qu’à partir du //**Entity **//
Class Etudiant
media on peut directement {
// …
savoir à quel étudiant il /**
* @ORM\OneToOne(targetEntity= "App\Entity\Media")
appartient nous devons faire */
private $media;
une relation bidirectionnelle // …
}
Media aussi doit référencer //**Entity **//
Class Media
Etudiant {
/**
* @ORM\OneToOne(targetEntity=
"App\Entity\Etudiant",mappedBy="media")
*/
private $etudiant;
// …
}
72
Gestion des relations entre les entités (5)
ManyToOne Unidirectionnelle
Relation unidirectionnelle //**Entity **//
Class Etudiant
puisque Section ne référence {
// …
pas Etudiant /**
* @ORM\ManyToOne(targetEntity="App\Entity\Section")
*/
private $section;
// …
}
//**Entity **//
Class Section
{
// …
}
73
Gestion des relations entre les entités (6)
OneToMany Bidirectionnelle
//**Entity **//
Si nous voulons connaitre dans l’objet section Class Section
{
l’ensemble des étudiants qui lui sont affectés alors // …
on doit avoir une relation bidirectionnelle /**
*@ORM\OneToMany(targetEntity="App\Entity\Etudiant",
Section aussi doit référencer Etudiant *mappedBy="section")
*/
private $etudiants;
On aura une relation OneToMany coté Section // …
public fonction __construct() {
puisqu’à « One » Section on a « Many » Etudiants. $this->etudiants = new ArrayCollection () ;
}
On doit ajouter l’attribut mappedBy côté }//**Entity **//
OneToMany et inversedBy côté ManyToOne Class Etudiant
{ //…
/**
On doit spécifier dans le constructeur du * @ORM\ManyToOne(targetEntity="App\Entity\Section",
OneToMany que l’attribut mappé est de type inversedBy="etudiants")
*/
ArrayCollection en l’instanciant private $section;
// …
}
74
Gestion des relations entre les entités (7)
ManyToMany
//**Entity **//
Relation unidirectionnelle Class Matiere
puisque Cours ne référence pas {
// …
Prof }
//**Prof**//
Class Etudiant
Ici on peut savoir quels sont { //…
/**
les cours de chaque étudiant * @ORM\ManyToMany(targetEntity="App\Entity\Matiere"")
mais pas l’inverse (on peut */
private $cours ;
l’extraire via une requête) /…
/**
* Constructor
*/
public function __construct()
{
$this->matieres = new
\Doctrine\Common\Collections\ArrayCollection();
}
75 }
Gestion des relations entre les entités (8)
ManyToMany Bidirectionnelle
On doit spécifier dans les deux constructeurs que l’attribut mappé est
de type ArrayCollection en l’instanciant
76
Gestion des relations entre les entités (9)
Cas particulier ManyToMany
Etudiant Cours
* *
Transformation
77
Exercice
Créer les entités suivantes ainsi que les relations qui les relient :
Job
Personne Personne
* designation
Job firstname description
name 1
Hobby age
*
79
Les événements de Doctrine (1)
Appelé aussi les callbacks du cycle de vie, ce sont des méthodes à exécuter par doctrine et dépendant
d’événement précis :
PrePersisit
PostPersist ( s’exécute après le $em->flush() non après $em->persist() )
PreUpdate
PostUpdate
/**
PreRemove
* Transfer
PostRemove
*
* @ORM\Table(name="transfer")
Afin d’informer doctrine qu’une entité contient des
*
clallbacks nous devons utiliser l’annotation @ORM\Entity(repositoryClass="AppBundle\
@ORM\HasLifecycleCallbacks() Repository\TransferRepository")
Ceci ne s’applique que lors de l’utilisation * @ORM\HasLifecycleCallbacks()
des annotations */
class Transfer
80
Les événements de Doctrine (2)
Pour informer Doctrine de l’existence d’un événement on utilise maintenant l’annotation sur
l’action à réaliser.
/**
* @PrePersist
*/
public function onPersist(){
$this->createdAt = new \DateTime('NOW');
}
81
Les traits et Les événements de Doctrine
Imaginons maintenant que nous voulons « sécuriser plusieurs de nos entités » et d’avoir un peu
d’historique. L’idée est d’avoir deux attributs qui sont createdAt et modifiedAt pour avoir toujours
une idée sur la création de notre entité et de sa dernière modification.
L’idée est de créer pour chacune des entités à suivre des lifecycle callback qui vont mettre à jour ces
deux attributs lors de la création (prePersisit) et la modification (preUpdate).
Est-ce normal de le refaire pour toutes les entités ?
Si la réponse est non, que faire alors ?
Les traits
82
Exercice
Créer le Trait qui permet la gestion de la date d’ajout et de modification d’une entité.
Associer le avec l’entité formation
Tester le
Symfony
Les formulaires
AYMEN SELLAOUTI
1
Introduction
Rôle très important dans le web
2
Qu’est ce qu’un formulaire Symfony
La philosophie de Symfony pour les formulaires est la suivante :
Vision 1
Un formulaire est l’image d’un objet existant
Le formulaire sert à alimenter cet objet.
Classe Exemple
{ Nom
private $id;
private $nom; Age
private $age;
}
Vision 2
Un formulaire sert à récupérer des informations indépendantes de n’importe quel objet.
3
Comment créer un formulaire
Méthodes de création de formulaire
La création du formulaire se fait de 2 façons différentes :
4
Comment créer un formulaire
FormBuilder
La création d’un formulaire se fait à travers le Constructeur de
formulaire FormBuilder
Exemple :
$monPremierFormulaire= $this->createFormBuilder($objetImage)
Pour indiquer les champs à ajouter au formulaire on utilise la méthode
add du FromBuilder
http://symfony.com/doc/current/reference/forms/types.html 5
Comment créer un formulaire
FormBuilder
La méthode add contient 3 paramètres :
1) le nom du champ dans le formulaire
2) le type du champ
3) un array qui contient des options spécifiques au type du champ
Exemple :
$monPremierFormulaire= $this->createFormBuilder($exemple)
->add(‘nom’ ,TextType::class)
->add(‘age’ , IntegerType::class)
6
Comment créer un formulaire
Récupérer le formulaire avec getForm()
Pour récupérer le formulaire crée, il faut utiliser la méthode getForm()
Exemple :
$monPremierFormulaire= $this->createFormBuilder($exemple)
->add(‘nom’ ,TextType::class)
->add(‘age’ , IntegerType::class)
->getForm();
7
Externalisation de la définition des formulaires
AbstractType
Afin de rendre les formulaires réutilisables, Symfony permet l’externalisation
des formulaires en des objets.
Convention de nommage : L’objet du formulaire doit être nommé comme
suit NomObjetType
Cet objet doit obligatoirement étendre la classe AbstractType
Deux méthodes doivent obligatoirement être implémentées :
buildForm(FormBuilderInterface $builder, array $options) qui est la
méthode qui va permettre la création et la définition du formulaire
Il y a aussi la méthode configureOptions qui permet de définir l’objet
associé au formulaire. Cette fonction est obligatoire si vous voulez associer
votre form à une classe.
8
Externalisation de la définition des formulaires
Commande de génération de formulaire
Maker permet aussi d’automatiquement générer la classe du formulaire
php bin/console make:form FormNameType
symfony console make:form FormNameType
Exemple :
symfony console make:form PersonneType
9
Externalisation de la définition des formulaires
Commande de génération de formulaire
10
Externalisation de la définition des formulaires
Commande de génération de formulaire
Maker vous demandera si votre formulaire est associé à une entité ou
non. Répondez selon votre besoin.
La récupération du formulaire au niveau des contrôleurs devient
beaucoup plus facile :
$form = $this->createForm(TacheType::class, $entity);
Le second parameter n’est pas obligatoire
11
Affichage du formulaire dans TWIG
CreateView
Afin d’afficher le formulaire crée, il faut transmettre la vue de ce
formulaire à la page Twig qui doit l’accueillir.
La méthode createView de l’objet Form permet de créer cette vue
Il ne reste plus qu’à l’envoyer à la page twig en question
Exemple :
$form= $this->createForm (ExempleType::class,$exemple) );
return $this->render('Rt4AsBundle:Default:myform.html.twig',
array('form'=>$form->createView()));
12
Affichage du formulaire dans TWIG
form
Deux méthodes permettent d’afficher le formulaire dans Twig :
1) Afficher directement la totalité du formulaire avec la méthode form
{{ form(nomDuFormulaire) }}
13
Customiser vos Form avec Bootstrap
Afin d’intégrer directement bootstrap sur vos formulair, il suffit de :
Spécifier à symfony dans le fichier twig.yml que vous voulez du
Bootstrap pour vos formes.
Informer la Twig qui contient vos formulaire qu’elle doit utiliser ce
thème la
twig:
default_path: '%kernel.project_dir%/templates'
form_themes: ['bootstrap_5_layout.html.twig']
14
Récupérer les données envoyées
La gestion de la soumission des formulaires se fait à l’aide de la
méthode handleRequest($request)
HandleRequest vérifie si la requête est de type POST. Si c’est le cas,
elle va mapper les données du formulaire avec l’objet affecté au
formulaire en utilisant les setters de cet objet. Si aucun objet n’est
mappé, vous pouvez récupérer directement ces données.
Cette fonction prend en paramètre la requête HTTP de l’utilisateur
qui est encapsulé dans Symfony au sein d’un objet de la classe Request
de HttpFoundation.
15
Récupérer les données envoyées
Vous pouvez récupérer les données envoyées via votre formulaire en
accédant au champ via la méthode getData() de l’objet form.
Exemple : $form->getData() retourne un tableau associatif avec les
données envoyées par le formulaire.
Chaque élément aura comme clé le contenu de l’attribut name dans le
formulaire. public function showFormAction(Request $request) {
$form->handleRequest($request);
if ($form->isSubmitted() ){
$data = $form->getData();
// ToDo
}
} 16
Exercice
Récupérer les données envoyées à travers le formulaire et afficher le résultat.
Affichage du formulaire dans TWIG
Les composants du formulaire
form_start() affiche la balise d'ouverture du formulaire HTML, soit
<form>. Il faut passer la variable du formulaire en premier argument, et les
paramètres en deuxième argument. L'index attr des paramètres, et cela
s'appliquera à toutes les fonctions suivantes, représente les attributs à
ajouter à la balise générée, ici le <form>. Il nous permet d'appliquer une
classe CSS au formulaire, ici form-horizontal.
Exemple : {{ form_start(form, {'attr': {'class': 'form-horizental'}}) }}
form_errors() affiche les erreurs attachées au champ donné en argument.
form_label() affiche le label HTML du champ donné en argument. Le
deuxième argument est le contenu du label.
18
Affichage du formulaire dans TWIG (3)
Les composants du formulaire (2)
form_widget() affiche le champ HTML
Exemple : {{ form_widget(form.title, {'attr': {'class': 'form-control'}}) }}
form_row() affiche le label, les erreurs et le champ.
form_rest() affiche tous les champs manquants du formulaire.
form_end() affiche la balise de fermeture du formulaire HTML
Remarque : Certains types de champ ont des options d'affichage
supplémentaires qui peuvent être passées au widget. Ces options sont
documentées avec chaque type, mais l'option attr est commune à tous les
types et vous permet de modifier les attributs d'un élément de formulaire.
19
Exercice
Reprenez le formulaire que vous avez crée et changer le en décortiquant
chaque champs.
Passer une URL à l’objet du formulaire
Ne pouvons pas accéder dans la classe AbstractType à la méthode
generateUrl afin de modifier l’action du formulaire, il faut donc procéder
ainsi :
Utiliser le troisième paramètre de la méthode createForm. C’est un
tableau associatif contenant un ensemble d’option. On peut y ajouter deux
clé :
action : pour ajouter l’url de l’action
method : si vous voulez modifier l’attribut method qui est par défaut à post.
22
Les principaux types dans le formulaire
Liste des types
Les formulaires sont composés d’un ensemble de champs
Chaque champ possède un nom, un type et des options
Symfony propose une grande panoplie de types de champ
Texte Choix Date et temps Divers Multiple Caché
TextType ChoiceType DateType CheckboxType CollectionTyp HiddenType
TextareaType EntityType DatetimeType FileType e CsrfType
EmailType CountryType TimeType RadioType RepeatedType
IntegerType LanguageType BirthdayType
MoneyType LocaleType
NumberType TimezoneType
PasswordType CurrencyType
PercentType
SearchType
http://symfony.com/doc/current/forms.html
RangeType… 23
Les principaux types dans le formulaire
Le type choice
Type spécifique aux champs optionnels (select,
boutons radio, checkboxs)
Pour spécifier le type d’options qu’on veut avoir il
faut utiliser le paramètre expanded. S’il est à false
(valeur par défaut) alors nous aurons une liste Expanded=true
déroulante. S’il est à true alors nous aurons des
boutons radio ou des checkbox qui dépendra du
paramètre multiple
Exemple :
http://symfony.com/doc/current/reference/forms/types/choice.html Expanded=false
24
Les principaux types dans le formulaire
Le type Entity
Champ choice spécial
Les choices (les options) seront chargés à partir des éléments d’une
entité Doctrine
->add('emploi',EntityType::class, array(
'class' => 'Tekup\BdBundle\Entity\Emploi',
'choice_label'=>'designation',
'expanded'=>false,
'multiple'=>true)
)
http://symfony.com/doc/current/reference/forms/types/entity.html
25
Personnaliser le choice label
Afin de personnaliser ce que vous voulez afficher dans vos choix, vous avez deux
solutions :
1. Définir la méthode magique to_string de votre entité, c’est la méthode appelé
par défaut en cas d’absence d’une information sur ce qu’il faut afficher.
2. Affecter à la propriété choice_label une callback function qui retournera la
chaine à afficher pour chaque enregistrement. Elle prendra en paramètre
l’instance de l’entité à traiter.
->add('formateur', EntityType::class, array(
'choice_label' => function(Formateur $formateur){
return (
sprintf( %s-%s", $formateur->getName(),
$formateur->getField())
);
})
26
EntityType query_builder
Afin de customiser la liste de choix de l’utilisateur vous pouvez utilisé
la propriété query_builder
$builder->add('users', EntityType::class, [
'class' => User::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'ASC');
},
'choice_label' => 'username',
]);
27
Exercice
Créer une méthode permettant d’ajouter une personne à travers le
formulaire.
Les principaux types dans le formulaire (4)
Le type country
Affiche la liste des pays du monde
La langue d’affichage est celle de la locale de votre
application (config.yml)
Exemple
->add('pays',CountryType::class)
http://symfony.com/doc/current/reference/forms/types/country.html
29
Ne pas afficher un champs du formulaire
Dans certains cas, vous n’avez pas envie d’afficher un champs de
votre entité. Prenons l’exemple de l’état. Par défaut et lorsque vous créer
une formation, vous voulez qu’elle soit active. Ce n’est pas un choix
dépendant du créateur.
Votre objet form contient une méthode remove (l’opposé de add) qui
permet de supprimer un champs.
Pensez à ajouter une valeur au champs supprimé ou bien ajouter une
valeur initiale au niveau de l’entité à cet attribut.
30
Les principaux types dans le formulaire (4)
Le type file
Le type file permet l’upload de n’importe quel type de fichier.
Créer un champ de ce type dans votre form et mettez l’option mapped à false.
Le champ permet de récupérer un objet de type uploadedFile contenant le path de
l’objet à uploader
Afin de récupérer ce champs utiliser votre objet form et accéder au paramètre
ayant le même nom que votre propriété. Ensuite via la méthode getData récupérer
votre objet. Exemple pour une propriété image : $monImage = $form[‘image']-
>getData();
Pour pouvoir gérer cet objet il faut le copier dans le répertoire web de votre projet
et de préférence dans un dossier spécifique pour vos images ou vos upload.
http://symfony.com/doc/current/reference/forms/types/file.html 31
Les principaux types dans le formulaire (4)
Le type file
Attribuer un nom unique à votre fichier pour ne pas avoir de problème lors de l’ajout de
fichier ayant le même nom (vous pouvez utiliser la méthode suivante md5(uiniqueid());
Pour récupérer le nom de votre file utiliser getClientOriginalName()
Pour récupérer l’extension vous pouvez utiliser la méthode guessExtension de votre objet
file.
Pour déplacer votre fichier utiliser la méthode move($pathsrc,$pathdest) de votre objet
file.
__DIR__ vous donne le path de l’endroit ou vous l’utilisez.
Vous pouvez créer un paramètre dans services.yml afin d’y stocker le path de votre dossier
et le récupérer dans le Controller avec la méthode getParameter(‘nom du paramètre’);
Remarque : %kernel.root_dir% vous permet de récupérer le path du dossier app
http://symfony.com/doc/current/reference/forms/types/file.html 32
Le type file
/** @var UploadedFile $file */
$file = $form->get('file')->getData();
if ($file) {
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
// this is needed to safely include the file name as part of the URL
$safeFilename = $slugger->slug($originalFilename);
$newFilename = $safeFilename . '-' . uniqid() . '.' . $file->guessExtension();
// Move the file to the directory where brochures are stored
try {
$file->move(
$this->getParameter('upload_directory'),
$newFilename
);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
} parameters:
upload_directory: '%kernel.project_dir%/public/uploads'
https://symfony.com/doc/current/controller/upload_file.html 33
Exercice
Ajoutez un champs image pour
l’entité Personne et mettez en place le
mécanisme d’upload de l’image.
Exercice
Ajouter la fonctionnalité de mise à jour d’une personne.
Les validateurs
Définition
Le validateur est conçu pour valider les objets selon des contraintes.
Le validateur de symfony est utilisé pour attribuer des contraintes sur les formulaires.
La validation peut être faite de plusieurs façons :
YAML (dans le fichier validation.yml dans le dossier /Resources/config du Bundle en question)
Annotations sur l’entité de base du formulaire
XML
PHP
La méthode isValide() du FORM déclenche le processus de validation
http://symfony.com/doc/current/reference/constraints.html
36
Exemple Validateur
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Table(name="personne")
*/
class Personne
{
/**
* @var int
* @ORM\Column(name="id", type="integer") Ici nous indiquons à Symfony qu’il
* @ORM\Id ne faut accepter que les fichiers
* @ORM\GeneratedValue(strategy="AUTO") dont le type est pdf
*/
private $id;
/**
* @Assert\File(mimeTypes = {"application/pdf"})
* @ORM\Column(name="path", type="string")
*/
// …
}
37
Les validateurs : Les annotations
Afin de pouvoir utiliser les annotations de validation il faut importer la class Constraints
use Symfony\Component\Validator\Constraints as Assert;
Syntaxe :
@Assert\MaContrainte(option1="valeur1", option2="valeur2", …)
Exemples :
@Assert\NotBlank( message = " Ce champ ne doit pas être vide ")
@Assert\Length(min=4, message="Le login doit contenir au moins {{ limit }} caractères.")
@Assert\Url()
38
Enlever la validation HTML
Afin d’enlever la validation html ajouter le mot clé novalidate à votre form
39
Les annotations : Les contraintes de base
Contrainte Rôle Options
NotBlank La contrainte NotBlank vérifie que la -
Blank valeur soumise n'est ni une chaîne de
caractères vide, ni NULL.
La contrainte Blank fait l'inverse.
Type La contrainte Type vérifie que la valeur type (option par défaut) : le type duquel
est bien du type donné en argument. doit être la valeur,
parmi array, bool,int, object
40
Les annotations : Nombre, date
Contrainte Rôle Options
Range La contrainte Range vérifie que la valeur ne min : nbre de car minimum
dépasse pas X, ou qu'elle dépasse Y. max : nbre de car maximum
minMessage : msg erreur nbre de car min
maxMessage : msg erreur nbre de car max
invalidMessage : msg erreur si non nmbre
Date vérifie que la valeur est un objet de -
type Datetime, ou une chaîne de type YYYY-
MM-DD.
Time vérifie qque c’est un objet de type Datetime, ou -
une chaîne type HH:MM:SS.
DateTime vérifie que c’est un objet de typeDatetime, ou
une chaîne de caractères du type YYYY-MM-
DD HH:MM:SS.
41
Les annotations : File
Contrainte Rôle Options
File La contrainte File vérifie que la valeur est un maxSize : la taille maximale du fichier.
fichier valide, c'est-à-dire soit une chaîne de Exemple : 1M ou 1k.
caractères qui pointe vers un fichier existant, mimeTypes : mimeType(s) que le fichier
soit une instance de la classe File (ce qui inclut doit avoir.
UploadedFile).
Image La contrainte Image vérifie que la valeur est maxSize : la taille maximale du fichier.
valide selon la contrainte précédente File (dont Exemple : 1M ou 1k.
elle hérite les options), sauf que les minWidth /maxWidth : la largeur minimale
mimeTypes acceptés sont automatiquement et maximale que doit respecter l'image.
définis comme ceux de fichiers images. Il est minHeight /maxHeight : la hauteur
également possible de mettre des contraintes minimale et maximale que doit respecter
sur la hauteur max ou la largeur max de l'image.
l'image.
42
Validation Exemple
/**
* @var string
* @Assert\Length(min="3",max="10",maxMessage="Trop c'est trop")
* @ORM\Column(name="nom", type="string", length=50)
*/
private $nom;
/**
* @var string
* @Assert\File(mimeTypes = {"application/pdf"},mimeTypesMessage="Le
fichier doit être du format PDF")
* @ORM\Column(name="path", type="string")
*/
private $path;
43
Valider des champs non mapés
Lorsque le champs que vous voulez valider est non mapé et que vous souhaiter le valider, il faut
ajouter un paramètre constraints dans le tableau d’option de votre méthode add.
44
Exercice
Ajouter les validateurs nécessaires à votre formulaire.
Symfony
Sécurité
AYMEN SELLAOUTI
1
Introduction
Un site est généralement décomposé en deux parties :
Partie public : accessible à tous le monde
Partie privée : accessible à des utilisateurs particuliers.
Au sein même de la partie privée, certaines ressources sont spécifiques à des
rôles ou des utilisateurs particuliers.
Nous identifions donc deux niveaux de sécurité :
Authentification Autorisation
2
Authentification
Processus permettant d’authentifier un utilisateur.
Authentifié : membre
3
Security Bundle
Le Bundle qui gère la sécurité dans Symfony s’appelle SecurityBundle.
Si vous ne l’avez pas dans votre application, installer le via la
commande
composer require security
4
Fichier de configuration security.yml
security:
# https://symfony.com/doc/current/security/authenticator_manager.html
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#c-hashing-passwords Le Firewall qui gère la
password_hashers: configuration de
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' l’authentification de vos
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers utilisateurs
providers:
users_in_memory: { memory: null }
firewalls: Cette partie assure
dev: que le débuggeur de
pattern: ^/(_(profiler|wdt)|css|images|js)/ Symfony ne soit pas
security: false bloqué
main:
lazy: true
provider: users_in_memory
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN } 5
La classe user
L’ensemble du système de sécurité est basé sur la classe User qui
représente l’utilisateur de votre application.
Afin de créer la classe User, utiliser la commande :
symfony console make:user
Si vous n’avez pas le MakerBundle, installer le.
Cette outils vous posera un ensemble de questions, selon votre besoin
répondez y et il fera tout le reste.
6
UserProvider
Le User Provider est un ensemble de classe associé au bundle Security de
Symfony et qui ont deux rôles
Récupérer le user de la session. En effet, pour chaque requête, Symfony
charge l’objet user de la session. Il vérifie aussi que le user n’a pas changé au
niveau de la BD.
Charge l’utilisateur pour réaliser certaines fonctionnalités comme la
fonctionnalité se souvenir de moi.
https://symfony.com/doc/current/security/user_provider.html 7
UserProvider
Afin de définir le userProvider que vous voulez utiliser passer par le fichier
de configuration security.yaml
providers:
users:
entity:
# the class of the entity that represents users
class: 'App\Entity\User'
# the property to query by - e.g. username, email, etc
property: email
https://symfony.com/doc/current/security/user_provider.html 8
Exercice
Reprenez votre diagramme de classe.
Job
Ajouter la classe user. Personne
* designation
Ajouter les relations nécessaires. firstname description
name 1
age
*
*
Hobby
designation
Encoder le mot de passe
Vous n’avez pas toujours besoin de mot de passe
En cas de besoin, vous pouvez configurer la manière avec lequel votre
mot de passe doit être géré dans le fichier security.yml.
# config/packages/security.yaml
security:
security:
encoders:
# ...
App\Entity\User:
password_hashers:
algorithm: auto
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# use your user class name here Avant Symfony 5,3
App\Entity\User:
# Use native password hasher, which auto-selects the best
# possible hashing algorithm (starting from Symfony 5.3 this is
"bcrypt")
algorithm: auto A partir de Symfony 5,3 10
Encoder le mot de passe
Le service responsable de l’encodage des mots de passe est le service
UserProviderEncoder (avant Symfony 5.3) ou UserPasswordHasher à
partir de Symfony 5.3 .
Afin de l’utiliser, et comme tous les services de Symfony, il suffit de
l’injecter.
private $userPasswordHasher;
public function __construct( UserPasswordHasherInterface $userPasswordHasher)
{
$this->userPasswordHasher = $userPasswordHasher;
}
13
Authentification et Firewall
Comme décrite dans la documentation, l’authentification dans Symfony ressemble à
de « la magie ».
En effet, au lieu d’aller construire une route et un contrôleur afin d’effectuer le
traitement, vous devez simplement activer un « authentication provider ».
« L’authentication provider » est du code qui s’exécute automatiquement avant
chaque appel d’un contrôleur.
Symfony possède un ensemble d’ « authentication provider » prêt à l’emploi. Vous
trouverez leur description dans la documentation :
https://symfony.com/doc/current/security/auth_providers.html
Dans la documentation, il est conseillé de passer par les « Guard Authenticator » qui
permettent un contrôle total sur toutes les parties de l’authentification.
14
Les Guard Authenticator
Un « Guard authenticator » est une classe qui vous permet un control
complet sur le processus d’authentification.
Cette classe devra ou implémenter l’interface AuthentiatorInterface ou
étendre la classe abstraite associée au besoin, e.g.
AbstractFormLoginAuthenticator en cas de formulaire
d’authentification ou AbstractGuardAuthenticator en cas d’api
La commande make:auth permet de générer automatiquement cette
classe.
Une fois lancée, cette commande vous demande si vous voulez créer
un « empty authenticator » ou un « login form authenticator ».
15
Guard Authenticator
Symfony 5.3
A partir de la version 5.3, vous devez implémenter uniquement les méthodes
authenticate et la méthode onAuthenticationSuccess.
Il y a aussi des méthodes optionnelles que vous pouvez surcharger :
supports
onAuthenticationFailure
start
Symfony utilises à partir de la version 5,3 un nouveau Authenticator based
Security
Pour la gestion des utilisateurs elle utilises Passport
16
Guard Authenticator
Symfony 5.3 public function authenticate(Request $request): PassportInterface
{
$username = $request->request->get('username', '');
$request->getSession()->set(Security::LAST_USERNAME, $username);
return new Passport(
new UserBadge($username),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token,
string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
} 17
Activer le guard
(Symfony 5.3)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
appLogin:
pattern: ^/login
custom_authenticator: App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
18
Se déconnecter
Afin de se déconnecter, il suffit d’ajouter la clé logout dans votre firewalls
configuration dans security.yaml.
Ajouter ensuite une méthode vide logout dans votre securityController avec la
route associé à votre méthode logout.
Vous pouvez débuger les autres options de logout avec la commande
symfony console debug:config security
/** firewalls:
* @Route("/logout", name="logout") main:
*/ logout:
public function logout() { path: logout
} 19
Exercice
Créer un LoginForm en utilisant la commande
symfony console make:auth.
Terminer les étapes définies par la commande à la fin de son exécution.
20
Récupérer le user dans le contrôleur
Afin de récupérer le user dans un contrôleur, il suffit d’utiliser la méthode helper getUser.
Utiliser ensuite sa méthode
21
Récupérer le user dans le service
Afin de récupérer le user dans un service, il suffit d’injecter le Securiy Service.
Utiliser ensuite sa méthode getUser
use Symfony\Component\Security\Core\Security;
class HelperService
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function sendMoney() {
$user = $this->security->getUser()
}
}
22
Exercice
Fait en sorte que le lien login de votre template vous envoi vers la page
de login.
Ajouter un lien pour le logout.
23
Register
Afin de permettre l’ajout ou l’enregistrement de vos utilisateurs, vous pouvez
utiliser la commande :
symfony console make:registration-form
Cette fonctionnalité n’a rien de particulier, elle permet tout simplement d’ajouter
un utilisateur dans votre base de données.
Vous pouvez personnaliser le contrôleur généré comme vous le voulez.
24
Authentification manuelle d’un utilisateur
Une fois l’utilisateur inscrit, vous pourrez l’authentifier d’une façon manuelle en
injectant le service UserAuthenticatorInterface.
Utiliser sa méthode authenticateUser qui prend en paramètre le user, votre
authenticator et l’objet request
public function register(Request $request, UserPasswordHasherInterface
$userPasswordHasherInterface, LoginFormAuthenticator $authenticator,
UserAuthenticatorInterface $userAuthenticator): Response
{
//…
if ($form->isSubmitted() && $form->isValid()) {
//…
// Authenticate user
// retourne un Objet Response, celui généré par la méthode onAuthenticationSuccess
return $userAuthenticator->authenticateUser($user, $authenticator, $request);
}
25
Autorisation
Processus permettant d’autoriser un utilisateur à accéder à une
ressource selon son rôle.
Le processus d’authentification suit deux étape.
1- Lors de l’authentification l’utilisateur est associé à un ensemble de
rôles.
2- Lors de l’accès à une ressource, on vérifie si l’utilisateur a le rôle
nécessaire pour y accéder.
26
Les Rôles
Chaque utilisateur connecté a au moins un rôle : le ROLE_USER
return array_unique($roles);
} 27
Définir les droits d’accès
Les droits d’accès sont définit de deux façons :
1- Dans le fichier security.yaml
2- Directement dans la ressource
28
Sécuriser les patrons d’url
La méthode la plus basique pour sécuriser une partie de votre application est de
sécuriser un patron d’url complet dans votre fichier security.yaml.
Ceci se fait sous la clé access_control. Chaque entrée est un objet avec comme clé :
-path : le pattern à protéger
-roles : les rôles qui peuvent accéder à ce pattern.
Lorsque vous essayer d’accéder à une ressource, Symfony cherche dans cette
rubrique s’il y a un matching avec la route recherché de haut vers le bas. Le premier
qu’il trouve lui permet de vérifier si vous avez le bon rôle pour accéder à la ressource
demandé ou non. L’ordre donc est très important.
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
https://symfony.com/doc/current/security/access_control.html 29
Exercice
Créer une action avec la route ’/admin’. Décommenter les
access_control et essayer deux scénarios.
1. Connecter vous en tant que USER et essayer d’accéder à cette route.
Que se passe t-il ?
2. Déconnecter vous et essayer d’y accéder. Que se passe t-il ?
Modifier votre classe UserFixture et faite en sorte d’ajouter un user
avec le ROLE_ADMIN. N’effacer rien de votre base.
Connecter vous en tant qu’admin. Essayer d’accéder à la route /admin.
Que se passe t-il ?
30
Exercice
Créer une action permettant d’inscrire des utilisateurs.
Créer une action pour l’admin lui permettant d’ajouter des utilisateurs
avec le ROLE qu’il veut.
31
Définir la route à activer en cas d’erreur
401
Par défaut lorsque un utilisateur non authentifié essaye d’accéder à une ressource
protégé, un page d’erreur apparait.
Cependant ce n’est pas le comportement standard. Ce qu’on veut généralement
c’est de le rediriger vers la page de login.
Pour ce faire, vous devez implémenter la méthode start.
A l’intérieur de cette méthode implémenter le comportement que vous voulez, c’est
cette méthode qui sera appelé en cas de 401.
https://symfony.com/doc/current/security/access_denied_handler.html#customize-the-unauthorized-response 32
Sécuriser Les contrôleurs
Vous pouvez directement sécuriser vos contrôleurs en utilisant :
1- Le helper denyAccessUnlessGranted(‘ROLE_*’);
2- En utilisant l’annotation @IsGranted(‘ROLE_*’)
/** /**
* Matches /blog exactly * Matches /blog exactly
* *
* @IsGranted("ROLE_ADMIN") * @Route("/blog", name="blog_list")
* @Route("/blog", name="blog_list") */
*/ public function list()
public function list() {
{ $this->denyAccessUnlessGranted("ROLE_USER");
// ... // ...
} }
33
Sécuriser un service
Afin de sécuriser un service, il suffit d’injecter le Securiy Service.
Utiliser ensuite sa méthode isGranted
use Symfony\Component\Security\Core\Security;
class HelperService
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function sendMoney() {
if ($this->security->isGranted("ROLE_ADMIN")){
// Todo Send Money
}
}
} https://symfony.com/doc/current/security/securing_services.html
34
Sécuriser vos pages TWIG
Si vous voulez vérifier le role de l’utilisateur avant d’afficher une ressource ou des
informations dans vos pages TWIG, utiliser la méthode is_granted(‘ROLE_*’)
{% if is_granted('ROLE_ADMIN') %}
<a href=« /admin">Administration</a>
{% endif %}
35
Exercice
Faite en sorte que le menu s’adapte au rôle de l’utilisateur connecté.
36
Hiérarchie de rôles
Vous pouvez définir une hiérarchie de rôles.
Dans le fichier security.yaml et sous la clé role_hierarchy, définissez le rôle
principale suivie de l’ensemble des rôles dont il hérite.
Un use case très récurant est quand l’admin possédé tout les droits, donc l’admin
devra hériter de tous les rôles.
role_hierarchy:
ROLE_COMMERCIAL: ROLE_AGENT
ROLE_SECRETARY: ROLE_COMMERCIAL
ROLE_ADMIN: [ROLE_PARTNER, ROLE_SECRETARY]
38