Symfony 5

Doctrine

Les prérequis

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/install

Les fixtures

Les fixtures

Toutes 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é" 

Les fixtures

Installation du composant

# Install les librairies nescessaire à l'utilisation de fixtures
composer require --dev orm-fixtures

Ouvrez un terminal et saisissez la commande :

Les fixtures

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

Les fixtures

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();
    }
}

Les fixtures

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:load

Les fixtures

Exercice :

  1. Créer une class de fixture "PizzaFixtures"
  2. Insérer 3 pizzas dans la base de données

Les fixtures

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();
    }
}

Les relations

Les relations

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

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

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 !

Les relations

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" !

Les relations

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;
    }
}

Les relations

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;
    }
}

Les relations

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();
    }
}

Les relations

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

Les relations

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 relation

Les relations

Exercice !

Créer une entité "Ingredient" avec les champs :

  1. "name" de type string
  2. "price" de type float

Les relations

Exercice !

Ajouter un un champ de relation à l'entité "Pizza" : "ingredients" en ManyToMany en utilisant la console symfony

Les relations

Exercice !

Créer une classe de fixtures "IngredientFixtures" et créer les ingrédients suivant :

  1. "Sauce Tomate" : 0.2 €
  2. "Tomates" : 0.5 €
  3. "Jambon" : 1 €
  4. "Olive" : 1 €
  5. "Chorizo": 1 €
  6. "Crème fraîche" : 0.4 €
  7. "Mozzarella" : 0.5 €
  8. "Gruyère" : 0.5 €

Les références de Fixtures

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

Les références 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');

Les références de Fixtures

Exercice !

  1. Dans "IngredientFixtures" définiser une référence pour chaque ingrédient
  2. Dans "PizzaFixtures" ajouter au moins 2 ingredients par Pizza !

Les références de Fixtures

Exercice !

Dans "templates/admin/pizza/list.html.twig" afficher les ingrédients de chaque pizza (sans leurs prix)

Les relations et les formulaires

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"

Les relations et les formulaires

É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,
    ])
;

Les relations et les formulaires

Exercice

Dans "AdminPizzaType" rajouter un champ "ingredients" qui est une sélection d'entités multiple !

Les fixtures Alice

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"

Les fixtures Alice

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
    
    

Les fixtures Alice

Installation d'Alice

# Lancer cette commande afin d'installer alice
composer require --dev hautelook/alice-bundle

Les fixtures Alice

Chargement des fixtures

# Lancer cette commande afin de d'insérer les fixtures
# dans la base de données
symfony console hautelook:fixtures:load

Les fixtures Alice

Exercice !

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

Exercice Final !

Pouvoir créer, lister, mettre à jour et supprimer des ingrédients !

Exercice Final !

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 Final !

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 Final !

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 Final !

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 Final !

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 Final !

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

Made with Slides.com