Doctrine
Afin de suivre cette formation assurez vous de bien avoir cloné le projet : PizzaLol
Dans un terminal exécuté les commandes suivante :
# Clone le projet
git clone https://github.com/Djeg/pizza-lol-app.git
# Se rendre dans le repertoire du projet
cd pizza-lol-app
# Aller chercher la branche doctrine/base
git fetch --all
git checkout sf5-doctrine/base
# Install les dépendances et la base de données
sh bin/installToutes applications nécessite un jeux de données préfabriqué afin de pouvoir fonctionner
Doctrine vient avec un composant de fixtures dont le rôle est de créer des données facilement pour le développement
Fixtures signifie "appareillé"
Installation du composant
# Install les librairies nescessaire à l'utilisation de fixtures
composer require --dev orm-fixturesOuvrez un terminal et saisissez la commande :
Le composant met à disposition la possibilité de créer des class de "Fixture"
Ces classes hérite de Doctrine\Bundle\FixturesBundle\Fixture
Elle possède une méthode "load" qui reçois une instance de Doctrine\Persistence\ObjectManager
Cet "ObjectManager" se comporte comme l'EntityManager de Doctrine
Ces classes sont situé dans le répertoire src/DataFixtures
exemple de Fixture :
namespace App\DataFixtures;
use App\Entity\Pizza;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class PizzaFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
// Création d'une pizza
$pizza = new Pizza();
$pizza->setName('Régina');
$pizza->setPrice(9.90);
// On persist la pizza dans le registre
// de doctrine
$manager->persist($pizza);
// On éxécute les requètes d'enregistrement
// dans la base de données
$manager->flush();
}
}Afin de créer et d'insérer des fixtures dans notre base de données nous pouvons utiliser la ligne de commande symfony depuis un terminal :
# Créer une nouvelle class de fixtures
symfony console make:fixtures
# Insére les fixtures dans notre base de données
# ATTENTION: Cette commande efface l'intégralité de
# notre base de données
symfony console doctrine:fixtures:loadExercice :
Correction :
<?php
namespace App\DataFixtures;
use App\Entity\Pizza;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Bundle\FixturesBundle\Fixture;
class PizzaFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$regina = new Pizza();
$regina->setName('Régina');
$regina->setPrice(9.7);
$vegan = new Pizza();
$vegan->setName('Végétarienne');
$vegan->setPrice(10.5);
$napolitan = new Pizza();
$napolitan->setName('Napolitaine');
$napolitan->setPrice(11.5);
$manager->persist($regina);
$manager->persist($vegan);
$manager->persist($napolitan);
$manager->flush();
}
}
Dans une base de données les relations sont le fait de lier une ou plusieurs tables entre elles
Doctrine utilise le même concept mais remplace les "table" par des entities
Il éxiste 3 grands types de relations
Les relations « ManyToMany »
Les relations « OneToMany » ou « ManyToOne »
Les relations « OneToOne »
Les relations « ManyToMany » associe plusieurs entités avec plusieurs entités
Par exemple, prenons notre pizzeria
On peut dire qu'une pizza possède plusieurs ingrédients
Et dans le sens inverse aussi, un ingrédient est contenues dans plusieurs pizzas !
Les relations « OneToMany » ou « ManyToOne » associe une entité avec plusieurs entités ou plusieurs entitès à une seul entité
Prenons l'exemple de notre pizzeria
Un utilisateur peut passer plusieurs commandes
Cependant une commande est associé à seulement un seul utilisateur !
Afin de définir un relation, Doctrine utilise des annotations
Nous pouvons écrire les annotations à la main
Ou bien demander à la console symfony de les remplir pour nous avec la commande "make:entity" !
Voici un éxemple d'annotation ManyToMany
namespace App\Entity;
use App\Repository\PizzaRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=PizzaRepository::class)
*/
class Pizza
{
/**
* @ORM\ManyToMany(targetEntity=Ingredient::class, mappedBy="pizzas")
*/
private ArrayCollection $ingredients;
public function getIngredients(): ArrayCollection
{
return $this->ingredients;
}
public function setIngredients(ArrayCollection $collection): self
{
$this->ingredients = $collection;
return $this;
}
public function addIngredient(Ingredient $ingredient): self
{
$this->ingredients->add($ingredient);
return $this;
}
public function removeIngredient(Ingredient $ingredient): self
{
$this->ingredients->removeElement($ingredient);
return $this;
}
}Voici un éxemple d'annotation ManyToMany
namespace App\Entity;
use App\Repository\IngredientRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=IngredientRepository::class)
*/
class Ingredient
{
/**
* @ORM\ManyToMany(targetEntity=Pizza::class, inversedBy="ingredients")
*/
private ArrayCollection $pizzas;
public function getPizzas(): ArrayCollection
{
return $this->pizzas;
}
public function setPizzas(ArrayCollection $collection): self
{
$this->pizzas = $collection;
return $this;
}
public function addPizza(Pizza $pizza): self
{
$this->pizzas->add($pizza);
return $this;
}
public function removePizza(Pizza $pizza): self
{
$this->pizzas->removeElement($pizza);
return $this;
}
}Pour utiliser une relation rien de plus simple !
public function listIngredients(Pizza $pizza): void
{
// Retourne tout les ingredients contenue dans une
// pizza
$ingredients = $pizza->getIngredients();
// Boucle sur les ingredients
foreach ($ingredients as $ingredient) {
echo $ingredient->getName();
}
}ArrayCollection est un type spécial de Doctrine
Il se comporte comme un Array !
Il permet d'optimiser les requêtes à la base de données
Il utilise les itérateur PHP
Nous pouvons facilement créer des relations en utilisant la console symfony :
# Met à jour notre entité
symfony console make:entity Pizza
# Il suffit alors de spécifier notre relationExercice !
Créer une entité "Ingredient" avec les champs :
Exercice !
Ajouter un un champ de relation à l'entité "Pizza" : "ingredients" en ManyToMany en utilisant la console symfony
Exercice !
Créer une classe de fixtures "IngredientFixtures" et créer les ingrédients suivant :
Lorsque l'on utilise des "Fixtures" nous pouvons définir des références
Ces références nous permettent de conserver une instance d'entité
Nous pouvons dès lors récupérer cette référence depuis un autre fichier de fixtures
Éxemple de référence dans IngredientFixtures
# Dans IngredientFixtures nous définissons une nouvelle
# référence
$this->setReference('Ingredient:' . $ingredient->getName(), $ingredient);
# Dans PizzaFixtures nous pouvons récupérer la référence :
$ingredient = $this->getReference('Ingredient:Sauce tomate');Exercice !
Exercice !
Dans "templates/admin/pizza/list.html.twig" afficher les ingrédients de chaque pizza (sans leurs prix)
Il existe un champs de formulaire dédié au relation
Ce champs est "Symfony\Bridge\Doctrine\Form\Type\EntityType"
Il permet à un formulaire de pouvoir attacher une ou plusieurs entités à notre "data_class"
Éxemple d'utilisation d'EntityType
// Dans un formulaire, par éxemple "AdminPizzaType"
// nous rajoutons un champs "ingredients" qui doit
// être une séléction d'entités :
$builder
->add('ingredients', EntityType::class, [
// Le class de l'entité que l'on veut attacher
'class' => Ingredient::class,
// La proprité de "Ingredient" qui sera affiché dans
// notre formulaire
'choice_label' => 'name',
// Est-ce que l'on peut séléctionner plusieurs
// entités ?
'multiple' => true,
])
;Exercice
Dans "AdminPizzaType" rajouter un champ "ingredients" qui est une sélection d'entités multiple !
Il existe un bundle Symfony nous permettant de définir des fixtures plus simplement !
Ce "bundle" c'est "hautelook/alice"
Il utilise le format yaml afin de définir simplement des fixtures
Ces fichiers yaml doivent se situé dans le répertoire "fixtures"
Voici un exemple de fichier yaml alice :
# Nous générons des Pizzas
App\Entity\Pizza:
# Nous donnons un nom à notre pizza
pizza_regina:
name: 'Régina'
price: 9.5
# Nous pouvons générer automatiquement
# plusieurs pizza à la fois !
pizza_automatique_{0..10}:
# Il est aussi possible de générer des mots
# et des nombre aléatoire !
name: '<word()>'
price: '<numberBetween(5, 14)>'
# Nous pouvons aussi définir des relations
ingredients:
- @ingredient_tomate
- @ingredient_saucisse
Installation d'Alice
# Lancer cette commande afin d'installer alice
composer require --dev hautelook/alice-bundleChargement des fixtures
# Lancer cette commande afin de d'insérer les fixtures
# dans la base de données
symfony console hautelook:fixtures:loadExercice !
Créer un fichier "fixtures/fixtures.yml"
Reprendre les pizza dans "src/DataFixtures/PizzaFixtures" et "src/DataFixtures/IngredientFixtures" et les rajouter dans le fichier "fixtures/fixtures.yml" en utilisant la syntax "yaml"
Supprimer le repértoire "src/DataFixtures"
Charger les fixtures dans la base de données
Pouvoir créer, lister, mettre à jour et supprimer des ingrédients !
Exercice 1: Lister les ingrédients
Créer un controller dans le répertoire "src/Controller/Admin" nommé "IngredientController"
Ajouter un méthode "list" avec une route "/admin/ingredients/list"
Récupérer tout les ingrédients de la base de données
Afficher les ingrédients dans un template "admin/ingredient/list.html.twig"
Exercice 2: Menu de navigation
Créer un template "admin/_menu.html.twig" avec un menu de navigation
Créer un template "base_admin.html.twig" dans le répertoire "admin"
Hériter de 'base.html.twig' dans "base_admin.html.twig" et inclure le menu
Retoucher tout les templates dans "admin/" et hériter de "base_admin.html.twig"
Exercice 3: Créer un ingrédient
Créer un form "AdminIngredientType" avec les champs "name", "price" et "submit"
Créer une méthode dans "IngredientController" nommé create attaché la route "/admin/ingredients/create"
Utiliser le formulaire (si valide, enregistrer l'ingrédient en utilisant doctrine et rediriger sur la liste des ingrédients, sinon afficher le template "admin/ingredient/create.html.twig")
Ajouter un lien dans "list.html.twig" vers la page "create.html.twig"
Exercice 4: Supprimer un ingrédient
Créer une méthode "delete" dans le controller "IngredientController" avec la route "/admin/ingredients/{id}/delete"
Supprimer l'ingrédient en utilisant doctrine
Rediriger vers la page "list" des ingrédients
Ajouter un lien dans "list.html.twig" afin de supprimer un ingrédient
Exercice 5: Mettre à jour un ingrédient
Créer une méthode "update" dans le controller "IngredientController" avec la route "/admin/ingredients/{id}/update"
Utiliser le formulaire "AdminIngredientType" afin de mettre à jour un ingrédient (si le formulaire est valide, enregistrer l'ingrédient en utilisant doctrine et rediriger vers la liste des ingrédients, sinon afficher le formulaire dans le template "update.html.twig")
Mettre à lien dans "list.html.twig" afin de pouvoir éditer un ingrédient.
Exercice 6: Bonus, une liste générique
Créer un template "admin/_list.html.twig" et placé le code qui liste des ingrédients ou des pizzas
Inclure et utiliser ce template dans la liste des ingrédients et des pizzas en utilisant des paramètres d'inclusion