Symfony 6
Les fondamentaux
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Présentation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Symfony est un « Framework » PHP dont l'objéctif est de proposer un cadre de travail complet et adapté à tout les besoins d'une application web.
Quelques liens utiles :
- Symfony documentation : https://symfony.com/doc/current/index.html
- Composer : https://getcomposer.org/
- Le site officiel de PHP : https://www.php.net/docs.php
Présentation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Symfony utilise PHP en "orienté objet". Il suit aussi les normes de développement PHP : PSR
Quelques liens utiles :
- Le site officiel de PSR : https://www.php-fig.org/psr/
- Le repo github de symfony : https://github.com/symfony
Présentation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Symfony en plus d'être un framework, c'est aussi des composants indépendant qui peuvent être utilisé dans n'importe quelle projet PHP !
Quelques liens utiles :
- Le composant Htttp Foundation
- Le composant de formulaire
- Le composant de routing
- Le composant PropertyAccessor
Installation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Voici les prérequis afin de commencer à développer en symfony 5 !
Installation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Vous pouvez vérifier l'installation en ouvrant un terminal et en entrant les commandes suivantes
# Affiche la version de php
php -v
# Affiche la version de composer
composer -v
# Affiche la version de l'utilitauire en ligne
# de commande de symfony
symfony -vInstallation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Si php n'est pas présent en ligne de commande vous pouvez suivre ces tutoriel afin de le rendre accessible depuis votre ligne de commande :
Installation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Afin de suivre cette formation il est recomandé d'utiliser vscode avec les extensions suivantes :
Installation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Afin de commencer la formation, rendez vous dans votre dossier contenant vos projets et ouvrer un terminal
# Créé un nouveau projet symfony
symfony new formation-app --webapp Installation
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Ouvrez maintenant votre éditeur de code VSCode sur le répertoire « formation-app ». (Fichier > Ouvrir un dossier).
Vous pouvez ouvrir un terminal (View > Terminal).
Vous pouvez créer de nouveau terminaux (Terminal > New Terminal)

Permet de changer de terminal
Configuration
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Afin de pouvoir commencer la formation il nous faut configurer notre application afin de pouvoir nous connécter à notre base de donnée
Ouvrez le fichier ".env" et changer la valeur de configuration de "DATABASE_URL" :
# .env
DATABASE_URL="mysql://root@127.0.0.1:3306/pizza-lol?serverVersion=5.7"Configuration
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Maintenant il nous faut créer la base de données de notre projet. Pour cela dans un terminal lancer la commande :
# Créer la base de données
symfony console doctrine:database:createConfiguration
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Maintenant nous somme prêt à lancer notre application ! Pour cela ouvrer un nouveau terminal et entrer la commande :
# Lance le server de dévelopement
symfony server:start --port=4444Configuration
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Vous pouvez maintenant visiter notre application :

Architecture d'un projet Symfony
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient les exécutables nécessaire pour manipuler symfony. La console symfony s'y trouve par éxample.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient l'intégralité des fichiers de configuration. Nous passerons un peu de temps dans ce répertoire afin de configurer "bundle" et packages "flex".
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient des images de machines virtuel. Nous n'avons pas besoin de ce répertoire pour le moment :)
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient les « Migrations », soit les changement apporter à notre base de données durant notre dévelopement.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient les fichiers qui seront servis par notre serveur HTTP. Nous ne passerons que très peu de temps dans ce répertoire, mais c'est le point de départ d'un projet Symfony !
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient le code source de notre application. C'est ici que nous passerons le plus clair de notre temps. L'intégralité de nos fichiers PHP se trouverons ici.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient nos templates twig. C'est ici que nous trouvons le html que doit produire notre application par exemple.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient nos test automatisé ! C'est ici que nous pouvons tester notre application et s'assurer qu'elle fonctionne parfaitement bien.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient les fichiers de traductions. Utile si nous voulons rendre notre application multilingue.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient des fichiers généré par Symfony. C'est ici que nous pouvons retrouver le cache par exemple.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient l'intégralité des « Librairies » php que notre application utilise. C'est ici que l'on peut trouver le code source de Symfony par exemple.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Les fichier ".env", ".env.local", ".env.test" contiennent la configuration de notre application. C'est ici que nous définissons comment se connecter à une base de données par exemple.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient la liste des fichiers qui doivent être ignoré par notre system de versioning (Git).
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

"composer.json" et "composer.lock" contiennent les références des librairies que nous utilisons. Nous pouvons ainsi consulter les librairies à installer ainsi que leurs versions.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient le configuration des machines virtuel. Nous n'utiliserons pas ce fichier dans cette formation.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient la configuration nécessaire à notre librairie de test "php unit".
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient un petit texte explicatif de l'application. C'est le fichier qui s'affiche par défaut sur github !
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony

Contient les références des "packages flex" installé sur l'application. Nous aurons l'occasion d'en reparler.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg
Architecture d'un projet Symfony
Contient nos controllers.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg

Architecture d'un projet Symfony
Contient nos entities doctrine.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg

Architecture d'un projet Symfony
Contient nos formulaires.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg

Architecture d'un projet Symfony
Contient nos repositories doctrine.
par David JEGAT - david.jegat@gmail.com - github.com/Djeg

MVC
Modèle Vue Controlleur
MVC: Model View Controller
Symfony utilise une « Architecture » qui permet d'organiser notre code grâce à 3 grands concept.
Cette architecture est très célèbre dans le millieu du web et porte le nom de MVC :
MVC: Model View Controller
Le Modèle (Model) représente les « données » de notre application.
Prenons notre pizzeria, nous pouvons imaginer un model "Pizza" qui contiendra le nom et le prix d'une pizza.
Symfony utilise la libraire « doctrine » afin de manipuler ces « données ».
Doctrine utilise lui aussi une architecture célèbre : Le Data Mapper Pattern.
MVC: Model View Controller
Un éxemple de « Model » : La pizza !
<?php
namespace App\Entity;
use App\Repository\PizzaRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PizzaRepository::class)]
class Pizza
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: "integer")]
private $id;
#[ORM\Column(type: "string", length: 255)]
private $name;
#[ORM\Column(type: "float")]
private $price;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
}
MVC: Model View Controller
La Vue (View) contient la représentation de nos données sous un format qu'une machine peut afficher.
Le format le plus célèbre est HTML !
Symfony utilise Twig afin de « Formatter » nos données en HTML.
MVC: Model View Controller
Éxemple de Vue (view) avec twig:
{% extends 'base.html.twig' %}
{% block title %}Hello PizzaController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<div class="row justify-content-between align-items-center mb-4">
<div class="col">
<h1>Liste des pizzas</h1>
</div>
<div class="col text-right">
<a href="{{ path("pizza_create") }}" class="btn btn-primary">Créer une nouvelle pizza</a>
</div>
</div>
<div class="row">
{% if not data|length %}
<h3 class="col text-center">Il n'y a pas encore de pizzas enregistré</h3>
{% endif %}
{% for unit in data %}
<div class="col col-md-6">
<div class="card">
<div class="card-body">
<p class="text-center">
La pizza <strong>{{ unit.pizza.name }}</strong> est à {{ unit.pizza.price }}€
</p>
</div>
<div class="card-footer">
<div class="row justify-content-center align-items-center mb-2">
<div class="col d-flex align-items-stretch">
<a
href="{{ path('pizza_update', { id: unit.pizza.id }) }}"
class="btn btn-primary col"
>
Modifier cette pizza
</a>
</div>
</div>
{{
form(unit.deleteForm, {
action: path('pizza_delete', { id: unit.pizza.id })
})
}}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
MVC: Model View Controller
Le contrôleur (Controller) contient le code qui vas « Gluer » nos modèles avec nos vue.
Il s'occupe de faire les différentes actions nécessaire afin d'afficher notre vue.
MVC: Model View Controller
Éxemple de Contrôleur (Controller) :
<?php
namespace App\Controller;
use App\Entity\Pizza;
use App\Form\PizzaType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class PizzaController extends AbstractController
{
/**
* List l'intégralité des pizzas disponible
*/
#[Route("/pizzas", name: "pizza_list", methods: ["GET"])]
public function list(): Response
{
$pizzas = $this
->getDoctrine()
->getRepository(Pizza::class)
->findAll();
$data = [];
foreach ($pizzas as $pizza) {
$data[] = [
'pizza' => $pizza,
'deleteForm' => $this
->createForm(PizzaType::class, $pizza, [
'delete' => true,
])
->createView(),
];
}
return $this->render('pizza/list.html.twig', [
'data' => $data,
]);
}
/**
* Créé une nouvelle pizza en utilisant le composant de formulaire
*/
#[Route("/pizzas/new", name: "pizza_create", methods: ["GET", "POST"])]
public function create(Request $request): Response
{
$pizza = new Pizza();
$form = $this->createForm(PizzaType::class, $pizza);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$task = $form->getData();
$manager = $this->getDoctrine()->getManager();
$manager->persist($task);
$manager->flush();
return $this->redirectToRoute('pizza_list');
}
return $this->render('pizza/create.html.twig', [
'form' => $form->createView(),
'pizza' => $pizza,
]);
}
/**
* Met à jour une pizza
*/
#[Route("pizza/{id}/update", name: "pizza_update", methods: ["GET", "POST"])]
public function update(Pizza $pizza, Request $request): Response
{
$form = $this->createForm(PizzaType::class, $pizza);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$task = $form->getData();
$manager = $this->getDoctrine()->getManager();
$manager->persist($task);
$manager->flush();
return $this->redirectToRoute('pizza_list');
}
return $this->render('pizza/update.html.twig', [
'form' => $form->createView(),
'pizza' => $pizza,
]);
}
/**
* Suprime une pizza
*/
#[Route("pizza/{id}/delete", name: "pizza_delete", methods: ["POST"])]
public function delete(Pizza $pizza, Request $request): Response
{
$form = $this->createForm(PizzaType::class, $pizza, [
'delete' => true,
]);
$form->handleRequest($request);
if (!$form->isSubmitted() || !$form->isValid()) {
return $this->redirectToRoute('pizza_list');
}
$manager = $this->getDoctrine()->getManager();
$manager->remove($pizza);
$manager->flush();
return $this->redirectToRoute('pizza_list');
}
}
Controller
Controller
Le controller est une classe qui contient au moins une méthode. Cette classe doit être obligatoirement suffixer par "Controller".
Chaque méthode de cette classe à pour objéctif de retourner une instance de « Response »
Les Controller doivent être localisé dans le répertoire: src/Controller
Controller
Éxemple de controller simple :
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function hello(): Response
{
return new Response('Hello World !');
}
}Controller
Afin de fonctionner la méthode de notre controller doit être attaché à une « Route »
Une route est une annotation php, ou une entré dans un fichier de configuration.
Une route contient un chemin (path), un nom ainsi que les méthodes HTTP autorisé (GET, POST, PUT, PATCH ....)
Controller
Exemple de route :
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HelloController
{
#[Route("/hello", name: 'hello', methods: ['GET'])]
public function hello(): Response
{
return new Response('Hello World !');
}
}Chemin de notre route
Le nom de notre route (note: toujours préfixer par "app" afin d'éviter les conflits de nommage)
Les méthodes HTTP autorisées pour cette route. Ici "GET"
Controller
Convention de nommage des routes
Le nom de la route doit toujours être préfixé par "app_"
Il doit ensuite reprendre le nom du controller sans le suffix "Controller" (ex avec "HelloController": "app_hello")
Finalement il doit reprendre le nom de la méthode : "app_hello_methode"
Controller
Convention de nommage des routes
Question : Pour un controller "PizzaController" et une méthode "list" comment dois-je nommé la route ?
Controller
Convention de nommage des routes
Question : Pour un controller "PizzaController" et une méthode "list" comment dois-je nommé la route ?
app_pizza_list
Controller
Les paramètres de la route
Une route peut posséder des variables comme par éxemple un identifiant.
Ces variables ce note {nomDeLaVariable} dans le chemin de la route
On peut récupérer la valeur de cette variable en la déclarant simplement dans les paramètres de notre méthode de controller
Controller
Les paramètres de la route
#[Route("/pizza/{id}/show", name: "app_pizza_show", methods: ["GET"])
public function show(int $id): Response
{
// Nous pouvons maintenant utiliser l'id envoyé en paramètre ici !
return new Response(sprintf("L'identifiant de la pizza est %s", $id));
}Exemple de paramètre :
Controller
Les paramètres de la route
Exemple de paramètre :
Le nom du paramètre: ici "id"
Le paramètre peut ensuite être récupéré simplement en l'ajoutant dans notre méthode. Attention les noms doivent être les même !
#[Route("/pizza/{id}/show", name: "app_pizza_show", methods: ["GET"])
public function show(int $id): Response
{
// Nous pouvons maintenant utiliser l'id envoyé en paramètre ici !
return new Response(sprintf("L'identifiant de la pizza est %s", $id));
}Controller
Il est possible dans n'importe quelle méthode de controller d'accéder à la « Request » en la rajoutant en paramètre de la méthode.
La request contient les informations envoyer par le client HTTP (headers, queries, data ...)
Controller
Exemple d'utilisation de la « Request » :
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HelloController
{
#[Route("/hello", name: "app_hello", methods: ["GET"])
public function hello(Request $request): Response
{
$name = $request->query->get('name', 'World');
return new Response(sprintf('Hello %s !', $name));
}
}Controller
Vous pouvez en apprendre plus sur la « Request » et la « Response » ici !
Controller
Il éxiste une class « AbstractController »
Cette classe contient des méthodes facilitant l'utilisation du controller.
Elle peut être hérité depuis nos controller afin de faciliter notre travail.
Controller
Hériter de AbstractController
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HelloController extends AbstractController
{
#[Route("/hello", name: "app_hello", methods: ["GET"])
public function hello(Request $request): Response
{
$name = $request->query->get('name', 'World');
return new Response(sprintf('Hello %s !', $name));
}
}Controller
Les controller peuvent être généré en utilisant une commande Symfony depuis un terminal !
# Créer un nouveau controller « HelloController »
symfony console make:controller helloController
Créer un Controller « HelloController »
Récupérer le nom en utilisant la Request:
$name = $request->query->get('name', 'le nom par défaut');Créer une méthode nommé "hello"
Retourner une nouvelle réponse avec le nom récupéré
Lui attacher une Route "app_hello_hello" avec le chemin "/hello"
Exercice !
Controller
Quelques méthodes utile de AbstractController
// Créer une response de redirection vers une autre route
return $this->redirectToRoute('app_home');
// Créer l'url compléte d'une route
$url = $this->generateUrl('app_home');
// Créer un formulaire
$form = $this->createForm(SomeType::class);
// Retourne une réponse avec le contenue d'un template twig
return $this->render('some/template.html.twig', [ 'name' => 'john doe' ]);Le Model
Doctrine
Le Model: Doctrine
Doctrine est la librairie utilisé afin de créer des modèles.
Les modèles sont représenté par des objets PHP que l'on nomme « Entity »
Chaque « Entity » est rattaché à une table de la base de données
Chaque propriétés de notre entité correspond à une colone de la table.
Ces colones sont configuré via des annotations PHP
Chaque entités se place dans le répertoire src/Entity
Le Model: Doctrine
Afin de « récupérer » des données depuis notre base de données, Doctrine utilise des objets nommés "Repository".
Ces « Repository » contientnt autant de méthodes que l'on souhaite.
Chaque méthodes récupère une ou plusieurs entités.
Les « Repository » permettent d’exécuter des requêtes à notre base de données.
Le Model: Doctrine
Petit schéma récapitulatif :
Entity
- contient les données
- C'est une simple class PHP
- représente une table de notre base de données
Repository
- permet de récupérer des entités depuis notre base de données.
- Enregistre et supprime les entités dans la base de données
Le Model: Doctrine
Générer une entité et son Repository
On peut utiliser la console symfony afin de générer nos entités et repository :
# Génére une entité Pizza dans src/Entity ainsi
# que son repository PizzaRepository dans src/Repository
symfony console make:entity PizzaLe Model: Doctrine
Exemple d'Entity : La Pizza
<?php
namespace App\Entity;
use App\Repository\PizzaRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PizzaRepository::class)]
class Pizza
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\Column(type: 'float')]
private $price;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
}
Le Model: Doctrine
Exemple d'Entity : La Pizza
<?php
namespace App\Entity;
use App\Repository\PizzaRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PizzaRepository::class)]
class Pizza
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\Column(type: 'float')]
private $price;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getPrice(): ?float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
}
Définie l'entité ainsi que le nom de la table en base de données. Attache le Repository
Définie une colone de type ID
Auto génére l'ID
Définie une colone de type string ou integer
Le Model: Doctrine
Exemple d'Entity : La Pizza
<?php
namespace App\Entity;
use App\Repository\PizzaRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PizzaRepository::class)]
class Pizza
{
// ...
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
Les entités contienent des getters et setters afin de récupérer et changer les données
Le Model: Doctrine
Enregistré une entité depuis un Controller qui hérite de AbstractController
<?php
namespace App\Controller;
// use ...
class PizzaController extends AbstractController
{
#[Route('/pizza/nouvelle', name: 'app_pizza_new', methods: ['GET'])]
public function newPizza(PizzaRepository $repository): Response
{
// création d'une nouvelle pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.9);
// Enregistrement de la pizza dans la base de données
// en utilisant le PizzaRepository
$repository->add($pizza, true);
return new Response(
"La pizza avec l'id {$pizza->getId()} à bien été enregistré"
);
}
}
Le Model: Doctrine
Enregistré une entité depuis un Controller qui hérite de AbstractController
<?php
namespace App\Controller;
// use ...
class PizzaController extends AbstractController
{
#[Route('/pizza/nouvelle', name: 'app_pizza_new', methods: ['GET'])]
public function newPizza(PizzaRepository $repository): Response
{
// création d'une nouvelle pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.9);
// Enregistrement de la pizza dans la base de données
// en utilisant le PizzaRepository
$repository->add($pizza);
return new Response(
"La pizza avec l'id {$pizza->getId()} à bien été enregistré"
);
}
}
On hérite de la class AbstractController
Le Model: Doctrine
Enregistré une entité depuis un Controller qui hérite de AbstractController
<?php
namespace App\Controller;
// use ...
class PizzaController extends AbstractController
{
#[Route('/pizza/nouvelle', name: 'app_pizza_new', methods: ['GET'])]
public function newPizza(PizzaRepository $repository): Response
{
// création d'une nouvelle pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.9);
// Enregistrement de la pizza dans la base de données
// en utilisant le PizzaRepository
$repository->add($pizza);
return new Response(
"La pizza avec l'id {$pizza->getId()} à bien été enregistré"
);
}
}
On injécte le PizzaRepository dans la paramètres de la fonction
Le Model: Doctrine
Enregistré une entité depuis un Controller qui hérite de AbstractController
<?php
namespace App\Controller;
// use ...
class PizzaController extends AbstractController
{
#[Route('/pizza/nouvelle', name: 'app_pizza_new', methods: ['GET'])]
public function newPizza(PizzaRepository $repository): Response
{
// création d'une nouvelle pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.9);
// Enregistrement de la pizza dans la base de données
// en utilisant le PizzaRepository
$repository->add($pizza, true);
return new Response(
"La pizza avec l'id {$pizza->getId()} à bien été enregistré"
);
}
}
On enregistre la pizza dans la base de données
Le Model: Doctrine
Récupérer des entités depuis notre controller en utilisant un Repository
<?php
namespace App\Controller;
// use ...
class PizzaController extends AbstractController
{
#[Route('/pizza/nouvelle', name: 'app_pizza_new', methods: ['GET'])]
public function newPizza(PizzaRepository $repository): Response
{
// Récupération d'une pizza par son id
$pizza = $repository->find(1);
// Récupération de la toutes les pizzas
$pizzas = $repository->findAll();
// Supprime une pizza !
$repository->remove($pizza, true);
// ...
}
}
Le Model: Doctrine
Mettre à jour notre base de données
Symfony fournie une suite de ligne de commande afin de gérer notre base de données :
# Créer la base de données
symfony console doctrine:database:create
# Supprime la base de données
symfony console doctrine:database:drop --force
# Met à jour le schéma de la base de données
symfony console doctrine:schema:update --force
Le Model: Doctrine
Exercice !
- Créer une entité et un repository Pizza
- ajouter les champs "name" (string) et "price" (float).
- Créer un controller "PizzaController"
- Créer une méthode "generate" avec une route "/pizza/generate" (suivre les conventions de nommage)
- Créer et enregistré une pizza en récupérant les données depuis la requête
- Retourner une réponse avec le contenue 'Ok {idDeLaPizza}'
Le Model: Doctrine
Petit Bonus ! Les ParamConverter
Lorsque l'on créé un paramètre de route le composant FrameworkExtraBundle permet de directement résoudre nos entités sana passer par un repository !
Il suffit pour cela de simplement "typer" notre paramètre
Ainsi que de s'assurer que le nom de notre paramètre de route est bien une propriété de notre entité
Le Model: Doctrine
Petit Bonus ! Les ParamConverter : Éxemple
#[Route('/pizza/{id}/show', name: 'app_pizza_show', methods: ['GET'])]
public function show(Pizza $pizza): Response
{
// Nous pouvons maintenant utiliser la pizza résolu
// depuis l'id dans notre route
return new Response(sprintf("La pizza %s", $pizza->getName()));
}Le Model: Doctrine
Petit Bonus ! Les ParamConverter : Éxemple
"id" est une propriété de l'entité Pizza
Symfony résous tout seul notre pizza en appelant "repository->find(..)" !
Si aucune pizza n'est présente dans la base de données, Symfony lève une erreur HTTP 404
#[Route('/pizza/{id}/show', name: 'app_pizza_show', methods: ['GET'])]
public function show(Pizza $pizza): Response
{
// Nous pouvons maintenant utiliser la pizza résolu
// depuis l'id dans notre route
return new Response(sprintf("La pizza %s", $pizza->getName()));
}La Vue
Twig
La Vue : Twig
La vue permet d'afficher nos données dans un format que la machine comprend.
Le format le plus célèbre dans le web est HTML. Mais il en existe d'autre ! (xml, yaml, json ...)
Afin d'afficher du html Symfony utilise un moteur de template : Twig
Il nous permet d'écrire et de mettre en forme nos données en HTML facilement, avec une syntaxe allégé.
La Vue : Twig
Les fichiers twig sont localisé dans le répertoire templates
Il porte le nom suivant: "monFichier.monFormat.twig"
Éxemple : "liste_produits.html.twig"
Ils doivent suivre une convention de nommage lorsqu'il sont attaché à un controller (nous verrons cela).
La Vue : Twig
Éxemple d'un fichier HTML en Twig
<html>
<head>
<title>Welcome to Symfony!</title>
</head>
<body>
<h1>{{ page_title }}</h1>
{% if user.isLoggedIn %}
Hello {{ user.name }}!
{% endif %}
{# ... #}
</body>
</html>La Vue : Twig
Quelques référence rapide :
{# Ceci est un commentaire twig #}
{# affiche le contenue d'une variable #}
<p>Bonjour {{ name }}</p>
{# Affiche un contenue selon certaines condition #}
{% if age > 18 %}
<p>Vous êtes majeur !</p>
{% else if age <= 4 %}
<p>Areee Aree Areeeee</p>
{% else %}
<p>Vous êtes mineur !</p>
{% endif %}
{# boucle sur les éléments d'un tableau #}
{% for name in items %}
<p>Vous possédez {{ name }} !</p>
{% endfor %}
{#
Accède à la propriété d'un object:
¨ Twig utilise les getters et setters afin d'obtenir
la propriété d'un object. La commande plus haut éxécute:
$user->getName(); ¨
#}
Bonjour {{ user.name }} !La Vue : Twig
Afficher un template depuis un controller que Hérite de AbstractController
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class TestController extends AbstractController
{
#[Route('/test/display', name: 'app_test_display')]
public function display(): Response
{
// Retourne une instance de Response avec le contenue html
// de notre template
return $this->render('some_folder/some_template.html.twig', [
// Définie les variables accessible dans notre template
// twig
'username' => 'john',
'email' => 'john@mail.com',
'is_major' => false,
]);
}
}La Vue : Twig
Convention de nommage des templates !
Correspond au nom de notre controller !
Correspond au nom de notre méthode !
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class TestController extends AbstractController
{
#[Route('/test/display', name: 'app_test_display')]
public function display(): Response
{
// Retourne une instance de Response avec le contenue html
// de notre template
return $this->render('some_folder/some_template.html.twig', [
// Définie les variables accessible dans notre template
// twig
'username' => 'john',
'email' => 'john@mail.com',
'is_major' => false,
]);
}
}La Vue : Twig
Exercice !
- Créer une méthode "list" dans le pizza controller
- Attacher une route "/pizza/list" avec un nom correct
- Récupérer toutes les pizzas en utilisant un repository doctrine
- Créer un template dans `templates/pizza/list.html.twig`
- Ajouter du HTML basic avec une instructions "for" sur les pizzas
- Rendre le template dans notre controller en lui donnant les pizzas
La Vue : Twig
Inclusion et Héritage de block
Twig est un très puissant moteur de template qui permet l'inclusion et l'héritage d'autre template.
La Vue : Twig
Inclusion et Héritage de block
Prenons un simple template : '_menu.html.twig'
<nav>
<ul>
<li>
<a href="#">Menu 1</a>
</li>
<li>
<a href="#">Menu 2</a>
</li>
<li>
<a href="#">Menu 3</a>
</li>
</ul>
</nav>La Vue : Twig
Inclusion et Héritage de block
nous pouvons inclure ce template ou nous le souhaitons en utilisant la fonction twig "include":
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Welcome !</title>
</head>
<body>
{#
affiche le menu à cette endroit !
Nous pouvons aussi envoyé des variables à notre template
inclue.
#}
{{ include('_menu.html.twig', { name: pizza.name }) }}
</body>
</html>
La Vue : Twig
Inclusion et Héritage de block
Les template que nous incluons avec la commande "include" doivent toujours être préfixé par un "_"
Éxemple :
_menu.html.twig
_header.html.twig
blog/_article_card.html.twig
pizza/_pizza_detail.html.twig
La Vue : Twig
Inclusion et Héritage de block
L'héritage est un system encore plus puissant basé sur l'utilisation de block.
La Vue : Twig
Inclusion et Héritage de block
Reprenons une page html simple : "base.html.twig"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Welcome !</title>
</head>
<body>
</body>
</html>
La Vue : Twig
Inclusion et Héritage de block
Définissons des block qui pourront être redéfinie plus tard :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block page_title %}Welcome !{% endblock %}</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
La Vue : Twig
Inclusion et Héritage de block
Maintenant créons un template qui hérite de notre base : "list.html.twig"
{% extends 'base.html.twig' %}La Vue : Twig
Inclusion et Héritage de block
Grace à ce extends nous pouvons réécrire des blocks :
{% extends 'base.html.twig' %}
{# nous redéfinissons le titre de le page #}
{% block page_title %}
Liste des pizzas - yum yum
{% endblock %}La Vue : Twig
Inclusion et Héritage de block
Attention, le HTML en dehors des blocks ne s'affichera jamais !
{% extends 'base.html.twig' %}
{#
ce template "extends" d'un template parent, le html
qui suit ne s'affichera jamais car il n'est pas dans un "block"
#}
<div>ne s'affichera jamais</div>
{# nous redéfinissons le titre de le page #}
{% block page_title %}
Liste des pizzas - yum yum
{% endblock %}La Vue : Twig
Inclusion et Héritage de block
Il faut obligatoirement intégrer notre html dans un block
{% extends 'base.html.twig' %}
{# affiche dans la balise body #}
{% block body %}
<div>S'affichera dans la balise body</div>
{% endblock %}
{# nous redéfinissons le titre de le page #}
{% block page_title %}
Liste des pizzas - yum yum
{% endblock %}La Vue : Twig
Exercice !
- Créer un fichier `templates/base.html.twig` avec 2 block "title" et "body"
- Retoucher le fichier "pizza/list.html.twig" pour qu'il hérite "base.html.twig" et redéfinisse les block "title" et "body".
La Vue : Twig
Quelques fonctions twig très utile !
{# inclue un autre template #}
{{ include('_some_template.html.twig', { name: 'john doe' }) }}
{# affiche le cheming complet d'une route #}
{{ path('app_home') }} {# /home par éxemple #}
{# affiche l'url compléte d'une route #}
{{ url('app_home') }} {# https://mon-site.com/home par exemple #}Les formulaires
Comment traiter des données
Les formulaires
Les formulaires permettent de gérer les données rentré par un utilisateur
Ils occupent une place centrale dans n'importe quelle application web !
Symfony fourni un composant Form
Ce composant permet de définir des champs, de les valider et finalement des les transformer en objet PHP
Les formulaires
Les formulaires sont représenté par des class de Type
Ces classes hérite de AbstractType
Elles possèdent une méthode "buildForm" qui définie les champs de notre formulaire
Elles possèdent une méthode "configureOptions" qui définie les options de notre formulaire comme par exemple l'objet PHP que nous voulons récupérer lorsque notre formulaire est valide
Les formulaires se situe dans le répertoire `src/Form`
Les formulaires
Un éxemple de formulaire : PizzaType
<?php
namespace App\Form;
...
class PizzaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('price', MoneyType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Pizza::class,
]);
}
}Les formulaires
Un éxemple de formulaire : PizzaType
<?php
namespace App\Form;
...
class PizzaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('price', MoneyType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Pizza::class,
]);
}
}Ajout d'un champ "name". Correspond à la propriété "name" de notre entité Pizza
Définie le type de champ, ici "Text"
Définie la class PHP que notre formulaire doit remplir
Les formulaires
Nous pouvons générer une class de formulaire très simplement en utilisant la ligne de commande de symfony :
# Créer un formulaire "PizzaType"
symfony console make:form pizzaLes formulaires
Utiliser un formulaire depuis un Controller
// Créer un formulaire avec aucune données
$form = $this->createForm(PizzaType::class);
// Créer un formulaire et préremplie les champs
// avec la pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.9);
$form = $this->createForm(PizzaType::class, $pizza);
// Vérifie si le formulaire a était envoyé
$form->isSubmitted();
// Vérifié si le formulaire est valide
$form->isValid();
// Remplie le formulaire avec les données de la requête
$form->handleRequest($request);
// Récupére la l'objet PHP qu'a rémplie notre formulaire
$pizzaValidated = $form->getData();
// Créer la vue de notre formulaire qui peut être
// envoyé à twig pour l'affichage
$view = $form->createView();Les formulaires
Exemple concret d'utilisation de formulaire dans un controller
// Création de notre objet PHP qui sera "remplie"
// par notre formulaire
$pizza = new Pizza();
// Création de notre formulaire
$form = $this->createForm(PizzaType::class, $pizza);
// Maintenant nous replissons le formulaire et notre
// objet php avec la requête
$form->handleRequest($request);
// Nous vérifions si le formulaire à bien était envoyé
// et que les données sont valide
if ($form->isSubmitted() && $form->isValid()) {
// On récupére l'objet PHP validé et remplie par notre
// formulaire
$validPizza = $form->getData();
// Nous enregistrons l'objet en base de données
$manager->persist($validPizza);
$manager->flush();
// Finalement on redirige l'utilisateur vers une
// page d'acceuil
return $this->redirectToRoute('app_home');
}
// Si le formulaire n'est pas valide alors nous
// l'affichons dans un template en récupérant la "View"
// de notre formulaire
$formView = $form->createView();
// Maintenant nous affichons le template avec le formulaire
return $this->render('some/template.html.twig', [
'form' => $formView,
]);Les formulaires
Afficher un formulaire dans un template twig
{# affiche l'intégralité du formulaire #}
{{ form(form) }}
{# affiche le début de la balise form #}
{{ form_start(form) }}
{# affiche la label d'un champ #}
{{ form_label(form.name) }}
{# affiche les erreurs d'un champ #}
{{ form_errors(form.name) }}
{# affiche le widget d'un champ #}
{{ form_widget(form.name) }}
{# Affiche le label, les erreurs et le widget d'un champ #}
{{ form_row(form.name) }}
{# Affiche le reste d'un formulaire #}
{{ form_rest(form) }}
{# Affiche la fin de la balise form #}
{{ form_end(form) }}Les formulaires
Exercice !
- Créer un formulaire "PizzaType" avec les champs "name", "price" et "submit" de type "TextType", "MoneyType" et "SubmitType"
- Créer une méthode dans PizzaController nommé "create"
- Créer une route pour cette méthode "/pizza/create"
- Utiliser le formulaire afin de soumettre et valider notre formulaire dans la méthode create de PizzaController
- Enregistrer l'entité dans doctrine et redirigé vers "app_pizza_list"
- Afficher le formulaire dans un template "pizza/create.html.twig" qui hérite de "base.html.twig"
Les formulaires
La protéction CSRF
Chaque formulaire créé un champ "hidden" avec un token CSRF
Ce champ est ensuite validé lorsque nous faisons "$form->isValid()"
Ce champ empêche les attaques CSRF, la falsification de nos formulaire et s'assure que chaque formulaire envoyé sois bien celui que nous avons créé au préalable
Les formulaires
La protéction CSRF
Par défaut tout les "Type" on une protection CSRF.
Les formulaires
Les options de formulaire
Chaque "Type" de formulaire, possède des options
Ces options permettent de configurer le type en question (son label, sa valeur etc ...)
Nous pouvons définir nos propres options grace à la méthode "configureOptions" de notre "Type"
Les formulaires
Les options de formulaire, quelques exemples avec le TextType
$builder
->add('name', TextType::class, [
// Définie le label de ce champ
'label' => 'Nom de la pizza',
// Indique si ce champ est requis ou pas
'required' => true,
// Indique si ce champ doit être "mappé" à notre objet PHP
'mapped' => true,
]);Les formulaires
Vous pouvez retrouver la liste compléte des Type et options disponible dans la documentation de Symfony !
Les formulaires
Les options personalisé
Les options personalisé se rajoute dans la méthode "configureOptions"
Elles permettent de créer des "Type" plus riche
Nous pouvons utiliser ces options dans la méthode "buildForm" afin de changer nos champs de formulaire
Les formulaires
Exemple d'une option personalisé pour créer un formulaire de suppression
class PizzaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (true === $options['delete_mode']) {
$builder->add('submit', SubmitType::class, [
'label' => 'Supprimer cette pizza',
]);
return;
}
$builder
->add('name', TextType::class)
->add('price', MoneyType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Pizza::class,
'delete_mode' => false,
]);
}
}Les formulaires
Exemple d'une option personalisé pour créer un formulaire de suppression
class PizzaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (true === $options['delete_mode']) {
$builder->add('submit', SubmitType::class, [
'label' => 'Supprimer cette pizza',
]);
return;
}
$builder
->add('name', TextType::class)
->add('price', MoneyType::class)
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Pizza::class,
'delete_mode' => false,
]);
}
}Ajout d'une option "delete_mode" qui vaut "false" par défaut
Si delete_mode est vrai, nous créons simplement un bouton d'envoie
Les formulaires
Envoyer des options à nos formulaires créé dans un controller
// Créé un formulaire PizzaType avec l'option
// "delete_mode" à vrai
$form = $this->createForm(PizzaType::class, $pizza, [
'delete_mode' => true,
]);Exercice Final
Pizza CRUD !
Exercice Final : Pizza Crud !
Prérequis
Basculer sur la branche "sf5-foundation/base" en exécutant les commandes suivantes :
# On commit notre espace de travail
git add .
git commit -m "exo symfony"
# Récupére les branches depuis github
git fetch --all
# Se rendre sur la branche sf5-foundation/base
git checkout sf5-foundation/baseExercice Final : Pizza Crud !
Objéctif
Pouvoir créer, lister, mettre à jour et supprimer des pizzas !
Exercice Final : Pizza Crud !
Créer une entité "Pizza" avec un champ "name" et "price" de type "string" et "float"
Créer un controller "AdminPizzaController"
Créer un FormType "AdminPizzaType"
Exercice Final : Pizza Crud !
Créer une méthode "list" dans AdminPizzaController avec une route "/admin/pizza/list"
Récupérer les pizzas depuis la base de données en utilisant le repository PizzaRepository
Afficher la list des pizzas dans un template "admin_pizza/list.html.twig"
Exercice Final : Pizza Crud !
Créer une méthode "create" dans AdminPizzaController
Attacher la route "/admin/pizza/create" à cette méthode
Créer et gérer les données de la requête avec le formulaire "AdminPizzaType"
Si le formulaire est envoyé et valide, enregistré la pizza en base de données et rediriger vers la liste des pizzas
Sinon, afficher la vue du formulaire dans un template "amin_pizza/create.html.twig"
Exercice Final : Pizza Crud !
Créer une méthode "update" dans AdminPizzaController
Attacher la route "/admin/pizza/{id}/update" à cette méthode
Créer et gérer les données de la requête avec le formulaire "AdminPizzaType"
Si le formulaire est envoyé et valide, enregistré la pizza en base de données et rediriger vers la liste des pizzas
Sinon, afficher la vue du formulaire dans un template "amin_pizza/update.html.twig"
Récupérer la pizza avec l'id envoyé depuis la route en utilisant le repository PizzaRepository
Exercice Final : Pizza Crud !
Créer une méthode "delete" dans AdminPizzaController
Attacher la route "/admin/pizza/{id}/delete" à cette méthode
Supprimer la pizza en utilisant le manager doctrine
Rediriger vers la liste des pizzas
Récupérer la pizza avec l'id envoyé depuis la route en utilisant le repository PizzaRepository
Les Relations
Les Relations
Doctrine met à disposition un puissant système de relation entre nos entitès
Les Possible relations sont au nombre de 3
- OneToOne : Lier une entité à une autre entité
- ManyToOne ou OneToMany : Lier une entité à plusieurs entités
- ManyToMany : Lier plusieurs entité à plusieurs entités
Les Relations
Pour mettre en place une relation il suffit de lancer la commande suivante :
symfony console make:entity PizzaLes Relations
Symfony vous demandera le nom de la propriété dans pizza qui contiendra notre relation :

Ici ingredients étant données que notre pizza possède plusieurs ingrédients
Les Relations
Maintenant choisisez le type de champs relation et laisser vous guider :)

Les Relations
Un fois cette commande terminé, symfony rendra possible l'accès à cette relation dans l'entité pizza (getIngredients etc ...) :
<?php
namespace App\Entity;
// use ....
#[ORM\Entity(repositoryClass: PizzaRepository::class)]
class Pizza
{
// ...
#[ORM\ManyToMany(targetEntity: Ingredient::class)]
private $ingredients;
/**
* @return Collection<int, Ingredient>
*/
public function getIngredients(): Collection
{
return $this->ingredients;
}
public function addIngredient(Ingredient $ingredient): self
{
if (!$this->ingredients->contains($ingredient)) {
$this->ingredients[] = $ingredient;
}
return $this;
}
public function removeIngredient(Ingredient $ingredient): self
{
$this->ingredients->removeElement($ingredient);
return $this;
}
}
Les Fixtures
Les Fixtures
Les fixtures permettent de créer une base données complète en très peu de temps avec des données de test !
Avoir des fixtures c'est s'assurer du bon fonctionnement de l'application, mais aussi pouvoir travailler en équipe :)
Pour "charger" ces fixtures, il éxiste un "bundle" (extension de symfony) :
Les Fixtures
Installation du bundle
symfony composer require hautelook/alice-bundleLes Fixtures
Créons notre premier fichier de fixtures afin de remplir notre base de données
Pour cela il nous faut créer le fichier "fixtures/data.yml" avec le contenu suivant :
# fixtures/data.yml
App\Entity\Ingredient:
ingredient1:
name: 'Mon premier ingredient'Les Fixtures
# fixtures/data.yml
App\Entity\Ingredient:
ingredient1:
name: 'Mon premier ingredient'Contient le nom de l"entité que nous voulons générer
Les Fixtures
# fixtures/data.yml
App\Entity\Ingredient:
ingredient1:
name: 'Mon premier ingredient'Contient un identifiant libre. Vous pouvez renseigner ce que vous souhaitez ici, cela nous sera utile pour les relations !
Les Fixtures
# fixtures/data.yml
App\Entity\Ingredient:
ingredient1:
name: 'Mon premier ingredient'Ici nous renseignons les "propriétés" de la class Ingredient que nous devons remplir
Les Fixtures
symfony console hautelook:fixtures:loadAfin de charger ce fichier dans notre base de données, une seule commande !
Les Fixtures
Il est possible avec Alice de générer "range" de données :
App\Entity\Ingredient:
ingredient{1..100}:
name: 'Mon Super Ingrédient'Les Fixtures
App\Entity\Ingredient:
ingredient{1..100}:
name: 'Mon Super Ingrédient'Génére 1 à 100 ingrédient !!
Les Fixtures
App\Entity\Ingredient:
ingredient{1..100}:
name: "<word()>"Il est aussi possible de générer de fausse données :
Vous retrouverez la liste des toutes les fausses données que vous pouvez générer ici
Alice utilise fakerPHP pour générer ses fausses données
Génére un mot aléatoire
Les Fixtures
App\Entity\Ingredient:
ingredient{1..100}:
name: "<word()>"
App\Entity\Pizza:
pizza1:
name: "Régina"
price: 8.8
ingredients: ["@ingredient1", "@ingredient2"]Il est aussi possible de gérer les relations facilement :
Les Fixtures
App\Entity\Ingredient:
ingredient{1..100}:
name: "<word()>"
App\Entity\Pizza:
pizza1:
name: "Régina"
price: 8.8
ingredients: ["@ingredient1", "@ingredient2"]Il est aussi possible de gérer les relations facilement :
Ici nous attachons 2 ingrédients à notre entité pizza : @ingredient1 et @ingredient2
Les Fixtures
App\Entity\Ingredient:
ingredient{1..100}:
name: "<word()>"
App\Entity\Pizza:
pizza1:
name: "Régina"
price: 8.8
ingredients: ["@ingredient*"]Il est aussi possible de gérer les relations facilement :
Il est aussi possible de choisir un ingrédient aléatoire en utilisant l'étoile : *
Repository
Le QueryBuilder
Repository
Le QueryBuilder
Afin de pouvoir récupérer des données de manière optimal doctrine à mis en place un outil : le QueryBuilder
Cette outil s'utilise à l'intérieur de nos repository et permet de récupérer des données filtré et ordonné
Repository
Le QueryBuilder
Pour utiliser un QueryBuilder rien de plus simple :
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('pizza.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Création d'un QueryBuilder
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Chaque QueryBuilder doit avoir un nom de votre choix ici: 'pizza'
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Ajout d'un order by. Le premier paramètre constitue le champ et le second la diréction
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
On limite les résultats à seulement 10
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Génération de la requête SQL
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
Récupération des données sous forme de tableaux d'entité Pizza
Repository
Le QueryBuilder
<?php
namespace App\Repository;
// use ...
class PizzaRepository extends ServiceEntityRepository
{
// ...
public function findLastTen(): array
{
$queryBuilder = $this->createQueryBuilder('pizza');
return $queryBuilder
->orderBy('book.id', 'DESC')
->setMaxResults(10)
->getQuery()
->getResult();
}
}
ATTENTION : Une méthode de répository doit toujours commencer par find
Repository
Le QueryBuilder
Afin de se protéger des injéctions SQL, le query builder possède un système de paramètres nommé :
$queryBuilder
->andWhere('pizza.name LIKE :name')
->setParameter('name', '%Reg%');Repository
Le QueryBuilder
$queryBuilder
->andWhere('pizza.name LIKE :name')
->setParameter('name', '%Reg%');Ajout d'une condition where. Ici si le text contient le paramètre données
Repository
Le QueryBuilder
$queryBuilder
->andWhere('pizza.name LIKE :name')
->setParameter('name', '%Reg%');Nous déclarons un paramètre avec la syntax :name
Repository
Le QueryBuilder
$queryBuilder
->andWhere('pizza.name LIKE :name')
->setParameter('name', '%Reg%');Nous remplaçons le paramètre :name par "%Reg%"
Graçe à ce remplacement, Doctrine sera capable de sécuriser le paramètre. Aucunes injéction SQL n'est possible
Repository
Le QueryBuilder
Il est aussi possible de faire des conditions et autre sur une relation !
$queryBuilder
->leftJoin('pizza.ingredients', 'ingredient')
->andWhere('ingredient.name = :name')
->setParameter('name', 'Tomate');Repository
Le QueryBuilder
$queryBuilder
->leftJoin('pizza.ingredients', 'ingredient')
->andWhere('ingredient.name = :name')
->setParameter('name', 'Tomate');Ici nous joignons les ingrédients de pizza en spécifiant un alias : "ingredient"
Repository
Le QueryBuilder
$queryBuilder
->leftJoin('book.ingredients', 'ingredient')
->andWhere('ingredient.name = :name')
->setParameter('name', 'Tomate');Maintenant nous pouvons rajouter une condition sur le nom d'ingrédient
Repository
Le QueryBuilder
$queryBuilder
->leftJoin('book.ingredients', 'ingredient')
->andWhere('ingredient.name = :name')
->setParameter('name', 'Tomate');Remplacement du paramètre nom afin d'éviter les ninjéctions SQL
Repository
Le QueryBuilder
$queryBuilder
->leftJoin('book.ingredients', 'ingredient')
->andWhere('ingredient.name = :name')
->setParameter('name', 'Tomate');Ici nous pouvons lire la requête comme ceci: Récupére les pizza dont l'un des ingrédients est nommé '"Tomate"
Symfony 5 - les fondamentaux
By David Jegat
Symfony 5 - les fondamentaux
- 1,098