Exemple Livres/Auteurs avec Doctrine

Vous pouvez reprendre ici une nouvelle application. On voudrait gérer une bibliothèque avec des Livres et des Auteurs. Les 2 entités se présentent ainsi :

_images/livre-auteur.png

Entités

Créons l’entité Auteur

php bin/console make:entity
  • Répondez aux questions pour ajouter des champs nom et prenom de type string dans Auteur.

  • Vérifiez la création du code correspondant dans src/Entity.

Puis faire de même avec l’entité Livre. Pour la relation, ajoutez un champ de type relation à l’entité Livre. Faites afficher toutes les possibilités avec le ? :

Class name of the entity to create or update (e.g. AgreeableGnome):
> Livre

created: src/Entity/Livre.php
created: src/Repository/LivreRepository.php

Entity generated! Now let's add some fields!
You can always add more fields later manually or by re-running this command.

New property name (press <return> to stop adding fields):
> titre

Field type (enter ? to see all types) [string]:
>

Field length [255]:
>

Can this field be null in the database (nullable) (yes/no) [no]:
>

updated: src/Entity/Livre.php

Add another property? Enter the property name (or press <return> to stop adding fields):
> annee

Field type (enter ? to see all types) [string]:
> ?

Main types
* string
* text
* boolean
* integer (or smallint, bigint)
* float

Relationships / Associations
* relation (a wizard 🧙 will help you build the relation)
* ManyToOne
* OneToMany
* ManyToMany
* OneToOne

Array/Object Types
* array (or simple_array)
* json
* object
* binary
* blob

Date/Time Types
* datetime (or datetime_immutable)
* datetimetz (or datetimetz_immutable)
* date (or date_immutable)
* time (or time_immutable)
* dateinterval

Other Types
* json_array
* decimal
* guid


Field type (enter ? to see all types) [string]:
> integer

Can this field be null in the database (nullable) (yes/no) [no]:
>

updated: src/Entity/Livre.php

Add another property? Enter the property name (or press <return> to stop adding fields):
> auteur

Field type (enter ? to see all types) [string]:
> relation

What class should this entity be related to?:
> Auteur

What type of relationship is this?
------------ -------------------------------------------------------------------
Type         Description
------------ -------------------------------------------------------------------
ManyToOne    Each Livre relates to (has) one Auteur.
            Each Auteur can relate to (can have) many Livre objects

OneToMany    Each Livre can relate to (can have) many Auteur objects.
            Each Auteur relates to (has) one Livre

ManyToMany   Each Livre can relate to (can have) many Auteur objects.
            Each Auteur can also relate to (can also have) many Livre objects

OneToOne     Each Livre relates to (has) exactly one Auteur.
            Each Auteur also relates to (has) exactly one Livre.
------------ -------------------------------------------------------------------

Relation type? [ManyToOne, OneToMany, ManyToMany, OneToOne]:
> ManyToOne

Is the Livre.auteur property allowed to be null (nullable)? (yes/no) [yes]:
> no

Do you want to add a new property to Auteur so that you can access/update Livre objects from it - e.g. $auteur->getLivres()? (yes/no) [yes]:
> yes

A new property will also be added to the Auteur class so that you can access the related Livre objects from it.

New field name inside Auteur [livres]:
>

Do you want to activate orphanRemoval on your relationship?
A Livre is "orphaned" when it is removed from its related Auteur.
e.g. $auteur->removeLivre($livre)

NOTE: If a Livre may *change* from one Auteur to another, answer "no".

Do you want to automatically delete orphaned App\Entity\Livre objects (orphanRemoval)? (yes/no) [no]:
>   no

Voici l’entité Livre obtenue :

<?php

   namespace App\Entity;

   use Doctrine\ORM\Mapping as ORM;

   /**
   * @ORM\Entity(repositoryClass="App\Repository\LivreRepository")
   */
   class Livre
   {
       /**
       * @ORM\Id()
       * @ORM\GeneratedValue()
       * @ORM\Column(type="integer")
       */
       private $id;

       /**
       * @ORM\Column(type="string", length=255)
       */
       private $titre;

       /**
       * @ORM\Column(type="integer")
       */
       private $annee;

       /**
       * @ORM\ManyToOne(targetEntity="App\Entity\Auteur", inversedBy="livres")
       * @ORM\JoinColumn(nullable=false)
       */
       private $auteur;

       public function getId(): ?int
       {
           return $this->id;
       }

       public function getTitre(): ?string
       {
           return $this->titre;
       }

       public function setTitre(string $titre): self
       {
           $this->titre = $titre;

           return $this;
       }

       public function getAnnee(): ?int
       {
           return $this->annee;
       }

       public function setAnnee(int $annee): self
       {
           $this->annee = $annee;

           return $this;
       }

       public function getAuteur(): ?Auteur
       {
           return $this->auteur;
       }

       public function setAuteur(?Auteur $auteur): self
       {
           $this->auteur = $auteur;

           return $this;
       }
   }

Interaction avec la BD

On lance le makemigrations suivi du migrate :

php bin/console make:migration
php bin/console doctrine:migrations:migrate

En cas de problème, on peut forcer la synchonisation du nouveau schéma de BD :

./bin/console doctrine:schema:update --force

Voir la doc correspondante

(Si easy_admin est installé, ajoutons un ou 2 auteurs puis livres dans easy_admin )

Relançons le make:crud

php bin/console make:crud

pour les entités Auteur et Livre puis améliorons un peu les templates proposés.

Template de base avec Bootstrap

On peut déjà améliorer le template de base base.html.twig avec Bootstrap, ici Bootswatch <https://bootswatch.com/> :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Bonjour{% endblock %}</title>
        {% block stylesheets %}
        <link rel="stylesheet" href="https://bootswatch.com/4/yeti/bootstrap.min.css">
        {% endblock %}
    </head>
    <body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#">App de Gestion Livres/Auteurs</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarColor01">
    <ul class="navbar-nav mr-auto">
    <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
    </li>
    <li class="nav-item">
        <a class="nav-link" href="#">Livres</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" href="#">Auteurs</a>
    </li>
    <li class="nav-item">
        <a class="nav-link" href="#">A propos</a>
    </li>
    </ul>
</div>
</nav>
        <div class="container">
        {% block body %}
        {% endblock %}
        </div>
        {% block javascripts %}
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
            integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
            crossorigin="anonymous">
        </script>
        <script src="https://bootswatch.com/_vendor/popper.js/dist/umd/popper.min.js">
        </script>
        <script src="https://bootswatch.com/_vendor/bootstrap/dist/js/bootstrap.min.js">
        </script>
        {% endblock %}
    </body>
</html>

on utilise les dépôts en ligne pour simplifier ici. Les liens sont à compléter …

Puis ensuite on fait hériter les autres templates de base.html.twig et on peut par exemple utiliser les boutons Bootstrap pour améliorer le rendu de la page index.html.twig des livres :

<button type="button" class="btn btn-info btn-sm">
<a href="{{ path('livre_show', {'id': livre.id}) }}">show</a>
</button>
<button type="button" class="btn btn-info btn-sm">
<a href="{{ path('livre_edit', {'id': livre.id}) }}">edit</a>
</button>

Si besoin, ajoutez une méthode __toString() à l’entité Auteur :

<?php
public function __toString()
{
    return $this->getPrenom() . '  ' . $this->getNom();
}

Faites de même dans l’entité Livre

Testez !