• 12 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 16/05/2024

Lisez les données en base

 

Récupérez vos entités avec les repositories

Après avoir mis les contraintes de validation au chapitre précédent et envoyé votre code sur les serveurs de test, vous recevez un e-mail d’Amélie.

Merci pour les corrections, je n’ai pas l’impression qu’il reste de problème de format. Par contre je n’arrive pas à être complètement sûre. Pourriez-vous me faire les pages d’affichage des livres, auteurs et éditeurs ? Merci d’avance.

Nous avons des données en base, et les moyens d’en ajouter encore. Cependant, Amélie a raison, il serait bien de pouvoir afficher ce que nous avons.

Au début de cette seconde partie, nous avons vu l’EntityManager, qui est le principal point d’entrée vers notre base de données pour les écrits. Nous allons maintenant voir le point d’entrée en lecture. Ou plutôt, les points d’entrée, car nous allons en avoir un par entité. Il s’agit des Repositories.

Vous aviez sans doute remarqué ces fichiers qui ont été créés en même temps que nos entités et qui se trouvent dans le dossier  src/Repository  . Il y en a un par entité :  BookRepository  pour l’entité  Book  ,  AuthorRepository  pour l’entité  Author  , etc.

À chaque fois que vous voudrez récupérer un certain type d’entité, vous ferez donc appel au Repository correspondant. De la même manière que vous avez injecté l’EntityManager dans vos controllers, vous pouvez injecter un ou plusieurs Repositories.

Mais si je veux récupérer un objet Book, il est lié à un ou plusieurs objets Author, et à un objet Editor ! Il me faut les trois Repositories à chaque fois ?

Non, quand même pas. Lorsque vous récupérez une entité, Doctrine fait automatiquement les jointures avec les entités qui lui sont liées, et vous recevez tous vos objets. Vous pourrez donc récupérer un objet Book depuis la base de données, puis récupérer l’objet Editor correspondant par un simple  $book->getEditor()  . 

Découvrez les méthodes des repositories

Avant cela, il nous faut récupérer nos entités depuis la base de données. Les Repositories étendent tous la même classe de base, qui leur fournit un certain nombre de méthodes en commun. La plupart sont d’ailleurs décrites dans les commentaires au début de chaque classe de Repository. Prenez par exemple  src/Repository/BookRepository.php  :

<?php
/**
* @extends ServiceEntityRepository<Book>
*
* @method Book|null find($id, $lockMode = null, $lockVersion = null)
* @method Book|null findOneBy(array $criteria, array $orderBy = null)
* @method Book[]    findAll()
* @method Book[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class BookRepository extends ServiceEntityRepository
{
// …

Nous avons donc :

  • find  : donnez un id, recevez l’entité correspondante.

  • findOneBy  : cette méthode prend en argument un tableau de critères pour effectuer une requête  WHERE  . Par exemple  findOneBy([‘title’ => ‘1984’])  .

  • findAll  : cette méthode renvoie l’intégralité des entités d’un certain type. Il est fortement déconseillé de s’en servir, sauf pour des tables de référence contenant un nombre très limité de lignes.

  • findBy  : cette méthode extrêmement polyvalente prend en paramètre le même tableau de critères que  findOneBy  , un second tableau optionnel construit de la même manière pour ajouter une clause  ORDER BY  , un paramètre optionnel  LIMIT  , et un paramètre optionnel  OFFSET  .

À ces méthodes s’ajoute une méthode de comptage :  count(array $criteria): int  .

Ouvrez le fichier  src/Controller/Admin/AuthorController.php  . Nous allons commencer par afficher tous les résultats grâce à  findAll  .

Mais on n’avait pas dit qu’on ne devait jamais faire ça ?

Si, tout à fait, mais nous changerons ça rapidement. En mettant notre controller à jour, cela donne donc quelque chose comme ça :

<?php
class AuthorController extends AbstractController
{
    #[Route('', name: 'app_admin_author_index', methods: ['GET'])]
    public function index(AuthorRepository $repository): Response
    {
        $authors = $repository->findAll();
        
        return $this->render('admin/author/index.html.twig', [
            'controller_name' => 'AuthorController',
            'authors' => $authors,
        ]);
    }
    // …

Il ne nous reste plus qu’à afficher cette liste dans notre template.

Affichez une liste d’objets

  • Vous pouvez passer une variable itérable (comme un array) à vos templates Twig sans avoir besoin de gérer l'affichage de chaque ligne à la main.

  • Pour cela, utilisez une boucle   {% for [valeur] in [array] %}...{% endfor %}  dans votre template Twig (ou sous la forme  {% for [clé], [valeur] in [array] %} … {% endfor %}  ).

  • Les boucles  for  de Twig incorporent une option  {% else %}  pour gérer l'affichage dans le cas où l'array est vide.

Comprenez le fonctionnement de la boucle  for  dans Twig

Pour accéder aux propriétés d’un objet ou d’un tableau en Twig, utilisez un point :  object.property  . Twig se chargera tout seul de trouver le bon moyen d'accéder à la propriété. Par exemple, si vous écrivez  book.title  dans Twig, ce dernier essaiera  $book['title']  , puis  $book->title  , puis  $book->title()  , avant d'obtenir un résultat grâce à  $book->getTitle()  . Cette approche fonctionne aussi si vous avez une méthode  $entity->isXxx()  ou  $entity->hasXxx()  .

Mettons à jour notre template  templates/admin/author/index.html.twig  avec une boucle et un peu de Bootstrap ; nous n’allons cependant pas afficher toutes les informations :

{% 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">
        <h1>Liste d'auteurs</h1>
        <ul class="list-group list-group-flush">
            {% for author in authors %}
                <div class="card mb-1">
                    <div class="card-body">
                        <div class="card-title d-flex justify-content-between">
                            <h4 class="mb-1">{{ author.name }}</h4>
                            <small class="text-muted">Identifiant : {{ author.id }}</small>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </ul>
    </div>
{% endblock %}

Retournez dans votre navigateur et observez le résultat.

Affichage de la liste d'auteurs
Affichage de la liste d'auteurs

Mais pourquoi est-ce qu’on n'affiche pas tout le détail de chaque auteur dans chaque carte ?

C’est une possibilité, mais ça risque de faire beaucoup d’informations dans un petit espace. Ce qui serait encore mieux, ce serait de pouvoir cliquer sur chaque élément de la liste pour accéder à une page qui en montrerait les détails.

Utilisez le routing pour récupérer une entité spécifique

Commençons par créer la page en question. Il nous faut une nouvelle méthode dans  Admin\AuthorController  , et une route pour aller avec. Appelons cette méthode et le template qui ira avec  show  .

Dans  src/Controller/Admin/AuthorController.php  :

<?php
// …
    #[Route('', name: 'app_admin_author_show', methods: ['GET'])]
    public function show(): Response
    {
        return $this->render('admin/author/show.html.twig');
    }

Et le template  templates/admin/author/show.html.twig  :

{% extends 'base.html.twig' %}

{% block title %}Détail d'un auteur{% 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">
        <h1>Auteur : </h1>
    </div>
{% endblock %}

Penchons-nous sur la route. Il nous faut un moyen pour indiquer quel auteur nous souhaitons afficher. Traditionnellement, dans les applications web, on passe l’identifiant de la ressource à afficher dans l’URL (par exemple /admin/author/5 pour l’auteur d’id 5). Il nous faut donc indiquer à Symfony qu’une partie du chemin associé à cette route est variable. Nous pouvons le faire grâce aux paramètres de routing. Écrivez simplement le nom de votre paramètre entre accolades dans le chemin, et réutilisez ce nom en tant qu’argument de votre méthode PHP :

<?php
#[Route('/{id}', name: 'app_admin_author_show', methods: ['GET'])]
public function show(int $id): Response

En l’état cependant, rien n’empêche un utilisateur de passer des lettres dans cette URL au lieu de chiffres représentant un identifiant. Bien sûr nous aurons une erreur grâce au typage PHP (  int  devant l’argument), mais ce n’est pas suffisant. Indiquons à Symfony que ce paramètre doit être constitué d’un ou plusieurs chiffres grâce à la clé  requirements  . Cette clé est un array. Dans cet array chaque clé est le nom d’un paramètre, et la valeur correspondante est l’expression régulière qui sert à le valider : 

<?php
#[Route('/{id}', name: 'app_admin_author_show', requirements: ['id' => '\d+'], methods: ['GET'])]
public function show(int $id): Response

Il nous reste à demander en paramètre notre  AuthorRepository  et à nous servir de sa méthode  find($id)  pour récupérer l’auteur qui correspond à cet identifiant…

Ou alors, nous pouvons profiter de la puissance de Symfony et d’un objet appelé  EntityValueResolver  , qui va directement aller chercher notre entité en fonction de l’identifiant passé dans l’URL. Pour ce faire, remplacez votre argument  id  dans la méthode du controller par un argument typé de la classe de votre entité.

Par exemple :

<?php
#[Route('/{id}', name: 'app_admin_author_show', requirements: ['id' => '\d+'], methods: ['GET'])]
public function show(?Author $author): Response

Notez le point d’interrogation devant  Author  qui signifie que cet argument peut aussi avoir comme valeur  null  . Si vous ne le mettez pas et que l’utilisateur demande un identifiant qui n’existe pas, il aura une erreur. En rendant l’argument nullable, vous demandez à Symfony de vous laisser gérer le cas où l’entité n’a pas été trouvée.

Affichez votre entité grâce à Twig

Nous pouvons maintenant passer notre variable  $author  à Twig pour aller l’afficher. 

<?php
#[Route('/{id}', name: 'app_admin_author_show', requirements: ['id' => '\d+'], methods: ['GET'])]
public function show(?Author $author): Response
{
    return $this->render('admin/author/show.html.twig', [
        'author' => $author,
    ]);
}

Profitons-en pour encapsuler notre affichage dans une balise  {% if %}{% endif %}  (à laquelle vous pourrez ajouter des  elseif  et  else  , bien entendu). Ça nous permettra de gérer le cas où l’utilisateur a demandé une entité qui n’existe pas en base de données :

<div class="example-wrapper">
    <h1>Auteur : </h1>
    {% if author is not null %}
        <div class="card mb-1 m-auto">
            <div class="card-body">
                <div class="card-title d-flex justify-content-between">
                    <h4 class="mb-1">{{ author.name }}</h4>
                    <small class="text-muted">Identifiant : {{ author.id }}</small>
                </div>
                <div class="d-flex justify-content-between card-text">
                    <ul class="list-group list-group-flush">
                        <li class="list-group-item">Date de naissance : {{ author.dateOfBirth }}</li>
                        <li class="list-group-item">Date de décès : {{ author.dateOfDeath }}</li>
                    </ul>
                    <p><small>Nationalité : {{ author.nationality }}</small></p>
                </div>
            </div>
        </div>
    {% else %}
        <div>Auteur non trouvé</div>
    {% endif %}
</div>

Allez voir sur https://127.0.0.1:8000/admin/author/0:

Affichage Auteur non trouvé
Affichage Auteur non trouvé

Jusqu’ici tout va bien. Maintenant retournez voir sur https://127.0.0.1:8000/admin/author et récupérez un identifiant qui existe dans la liste.

Retournez sur 
https://127.0.0.1:8000/admin/author/<identifiant>  en remplaçant  <identifiant>  par celui que vous avez repéré :

Affichage d'une erreur
Affichage d'une erreur

Que nous dit l’erreur ?

Que les objets de la classe  DateTimeImmutable  ne peuvent pas être convertis en chaîne de caractères. Et la ligne en surbrillance nous montre l’origine de l’erreur. Effectivement, nous essayons d’afficher la date de naissance, mais c’est un objet. Et d’après la documentation PHP, cet objet ne dispose pas de la méthode magique  __toString  qui nous aurait permis de l'afficher simplement. 

Utilisez les filtres et fonctions de Twig pour afficher des données complexes

Pas de problème, Twig a ce qu’on appelle un filtre pour ça. Il suffit de rajouter une barre verticale derrière le nom de la variable à filtrer, suivie du nom du filtre. En l’occurrence, ce sera le filtre  date  , auquel nous allons passer le format de date à utiliser : 

<li class="list-group-item">Date de naissance : {{ author.dateOfBirth|date('d M Y') }}</li>

Pour la date de décès c’est un peu plus compliqué, vu qu’elle peut être nulle. Nous allons donc devoir utiliser une condition, sous la forme d’un ternaire, comme PHP.

<li class="list-group-item">Date de décès : {{ author.dateOfDeath is not null ? author.dateOfDeath|date('d M Y') : '-' }}</li>

Ici il faut lire “Si  author.dateOfDeath  n’est pas nulle, affiche-la après l’avoir passée dans le filtre date ; sinon, affiche un tiret”. Et effectivement, si vous rafraîchissez la page, tout fonctionne !

Affichage du résultat
Affichage du résultat

On peut même faire mieux. Il existe une extension de Twig qui prend en charge l’internationalisation, c’est-à-dire le support des différentes langues et traductions. Ouvrez votre terminal et entrez la commande  symfony composer require twig/intl-extra  . Ensuite, retournez dans notre template, et rajoutez le filtre  country_name  derrière  author.nationality  :

<p><small>Nationalité : {{ author.nationality|country_name }}</small></p>

Rafraîchissez de nouveau votre page et admirez le résultat : les codes pays ont été remplacés par les noms des pays dans notre locale.

Affichage du résultat avec le pays
Affichage du résultat avec le pays

Utilisez la fonction  path  pour ajouter de la navigation

Nous pouvons maintenant faire le lien entre notre liste et cette page. Retournez sur le template  templates/admin/author/index.html.twig  . Nous allons envelopper nos titres de cartes dans des balises de lien  <a>  avec quelques classes Bootstrap. Mais que mettre dans l’attribut  href  ? Eh bien Twig dispose d’une fonction spéciale,  path  . Celle-ci prend en premier paramètre le nom d’une route de votre application, et en second paramètre optionnel les paramètres à passer à cette route. Donc, en passant  path(‘app_admin_author_show’, {id: author.id})  à  href  , nous créons un lien vers la bonne page à chaque fois :

<div class="card-title d-flex justify-content-between">
    <a href="{{ path('app_admin_author_show', {id: author.id}) }}" class="stretched-link link-dark">
        <h4 class="mb-1">{{ author.name }}</h4>
    </a>
    <small class="text-muted">Identifiant : {{ author.id }}</small>
</div>

Et voilà, vous pouvez aller de votre liste à vos pages de détail. Nous pourrons même rajouter un bouton sur la page tout en bas de  templates/admin/author/show.html.twig  pour revenir à la liste :

<a href="{{ path('app_admin_author_index') }}" class="btn btn-primary">Retour</a>

Parfois cependant, les méthodes de base de nos repositories ne sont pas suffisantes. Comment faire par exemple, si nous souhaitons rechercher tous les livres publiés entre deux dates ? Le paramètre de critères de  findBy  et  findOneBy  ne gère que les égalités. Ou encore, comment faire pour paginer nos résultats sur notre liste ?

Créez des requêtes spécifiques

Pour ce genre de cas, nous avons besoin d’avoir plus de contrôle sur les requêtes effectuées en base de données. Nous allons donc devoir rajouter des méthodes de requête dans notre Repository.

Ouvrez donc  src/Repository/AuthorRepository.php  . Commençons par y ajouter une nouvelle méthode pour filtrer les auteurs par date de naissance. Nous allons donc l’appeler  findByDateOfBirth  . Pour bien faire, cette méthode prend en paramètre un array qui contiendra deux clés optionnelles :  start  et  end  , pour la date de début et la date de fin de notre recherche, et retourne un array de résultats.

<?php
class AuthorRepository extends ServiceEntityRepository
{
    // …
    public function findByDateOfBirth(array $dates = []): array
    {
        // La recherche se fera ici
    }

Comprenez le  QueryBuilder  de Doctrine

Il y a maintenant plusieurs méthodes que vous pouvez utiliser pour construire vos requêtes personnalisées. La plus simple est d’utiliser un objet appelé  QueryBuilder  . Il est là pour nous aider à construire nos requêtes. En fait, nous allons appeler des méthodes très expressives sur notre objet  QueryBuilder  , comme  where()  ou même  join()  . Ensuite, lorsque nous demanderons à récupérer notre requête avec  getQuery()  , il construira la requête tout seul à partir de ce que nous avons demandé.

Commençons par le créer. Une méthode existe pour ça dans tous nos Repositories. Nous allons l’appeler et lui passer l’alias de notre entité (qui sera habituellement la première lettre du nom de notre entité) :

<?php
// …
public function findByDateOfBirth(array $dates = []): array
{
    $qb = $this->createQueryBuilder('a');
    // …

Par convention, il est très souvent appelé  $qb  . Si nous souhaitons ajouter une clause  WHERE  à notre requête, nous pouvons appeler la méthode  andWhere  sur ce QueryBuilder en lui passant nos critères de recherche. 

Par exemple, si nous souhaitons ajouter que la date de naissance de nos auteurs doit être supérieure ou égale à une date passée en paramètre :

<?php
public function findByDateOfBirth(array $dates = []): array
{
    $qb = $this->createQueryBuilder('a')
            ->andWhere('a.dateOfBirth >= :start')
            ->setParameter('start', new \DateTimeImmutable($dates['start']));
    // …

Nous pouvons maintenant lui demander de générer la requête correspondante, et de nous retourner les résultats avec  $qb->getQuery()->getResult()  :

<?php
public function findByDateOfBirth(array $dates = []): array
{
    $qb = $this->createQueryBuilder('a')
            ->andWhere('a.dateOfBirth >= :start')
            ->setParameter('start', new \DateTimeImmutable($dates['start']));
            
    return $qb->getQuery()->getResult();
}

Si nous ajoutons des conditions pour vérifier si nous avons une date de départ, de fin, ou les deux, ainsi que le tri de nos auteurs par date de naissance, on obtient ce résultat :

<?php
public function findByDateOfBirth(array $dates = []): array
{
    $qb = $this->createQueryBuilder('a');
    
    if (\array_key_exists('start', $dates)) {
        $qb->andWhere('a.dateOfBirth >= :start')
            ->setParameter('start', new \DateTimeImmutable($dates['start']));
    }
    
    if (\array_key_exists('end', $dates)) {
        $qb->andWhere('a.dateOfBirth <= :end')
            ->setParameter('end', new \DateTimeImmutable($dates['end']));
    }
    
    return $qb->orderBy('a.dateOfBirth', 'DESC')
            ->getQuery()
            ->getResult();
}

Utilisez vos méthodes de requêtes personnalisées

Retournez maintenant dans  src/Controller/Admin/AuthorController.php  et utilisez votre nouvelle méthode. Pour obtenir des dates de départ et de fin, passez-les simplement dans l’URL en paramètre, par exemple https://127.0.0.1:8000/admin/author?start=01-01-1975  , et récupérez l’information dans l’objet  Request  .

<?php
#[Route('', name: 'app_admin_author_index', methods: ['GET'])]
public function index(Request $request, AuthorRepository $repository): Response
{
    $dates = [];
    if ($request->query->has('start')) {
        $dates['start'] = $request->query->get('start');
    }
    
    if ($request->query->has('end')) {
        $dates['end'] = $request->query->get('end');
    }
    
    $authors = $repository->findByDateOfBirth($dates);
    // Le reste ne change pas

Faites un essai dans votre navigateur. Ici j’ai filtré les auteurs pour ne garder que ceux nés en 1975 :

Affichage des auteurs filtrés sur l'année de naissance 1975
Affichage des auteurs filtrés sur l'année de naissance 1975

Ajoutez un système de pagination dans Symfony avec Pagerfanta

  • Vous pouvez ajouter de la pagination facilement à votre application Symfony grâce à Pagerfanta.

  • Vous devrez installer plusieurs dépendances en fonction de votre projet. Pour Symfony :  babdev/pagerfanta-bundle  ,   pagerfanta/doctrine-orm-adapter  et  pagerfanta/twig  .

  • On crée un objet  Pagerfanta  en lui passant notre QueryBuilder Doctrine, le numéro de la page sur laquelle nous nous trouvons au sein de la pagination, et le nombre d'objets par page.

  • L'objet créé par Pagerfanta peut être parcouru comme le tableau d'objets récupérés en base de données que vous aviez à l'origine, pas besoin de modifier vos templates.

  • pagerfanta/twig  vous permet d'ajouter une fonction Twig  {{ pagerfanta([array]) }}  qui gère toute seule l'affichage des liens de navigation de votre pagination.

  • Pagerfanta embarque aussi un template de liens de navigation adaptés à Bootstrap.

Créez un formulaire d’édition pour vos entités

Vous pouvez même combiner ces routes dynamiques avec nos formulaires ! Rajoutez une deuxième route pour l’édition sur votre méthode  new  dans  src/Controller/Admin/AuthorController.php  , et ajoutez un argument nullable  ?Author $author  dans la méthode : 

<?php
#[Route('/new', name: 'app_admin_author_new', methods: ['GET', 'POST'])]
#[Route('/{id}/edit', name: 'app_admin_author_edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
public function new(?Author $author, Request $request, EntityManagerInterface $manager): Response

On peut tout à fait avoir deux routes pour le même controller ! Dans un cas, lorsqu’on appelle   /admin/author/new  , la variable  $author  vaudra  null  , et si on appelle  /admin/author/<identifiant>/edit  , Symfony ira chercher en base de données l’auteur correspondant et le mettra dans  $author  . Du coup, nous devons gérer le cas où  $author = null  . Modifiez aussi la première ligne de la méthode en ajoutant un opérateur de fusion null (  ??=  ).

<?php
public function new(?Author $author, Request $request, EntityManagerInterface $manager): Response
{
    $author ??= new Author();
    // …

Cet opérateur signifie que si  $author  vaut  null  , on lui assignera comme valeur une nouvelle instance de  Author  . Sinon, on garde celle qui a été passée en argument.

Cela veut donc dire que désormais, lorsqu’on appelle  /admin/author/new  on tombe sur un formulaire vide, mais si on appelle  /admin/author/<un_identifiant>/edit  , on tombe sur un formulaire d’édition prérempli ! Nous allons en profiter pour rendre le titre de la page conditionnel et parler d’une variable magique. 

Dans Twig, dans Symfony, il existe une variable nommée  app  qui est toujours disponible dans tous vos templates. Cette variable contient plein de choses intéressantes, comme la requête en cours. On peut aussi y trouver le nom de la route qui a été appelée grâce à  app.current_route  .

Profitons-en pour modifier le titre de notre page : Si la route appelée est  app_admin_author_new  , nous afficherons “Ajout”, sinon ce sera “Édition”. Nous pouvons écrire cette condition sous forme d’un ternaire, et mieux encore, stocker le résultat dans une variable une fois pour toute la page grâce au mot-clé  set  . Ouvrez  templates/admin/author/new.html.twig  :

{% extends 'base.html.twig' %}

{% set action = app.current_route == 'app_admin_author_new' ? 'Ajout' : 'Édition' %}

{% block title %}{{ action }} d'auteur{% endblock %}

{% block body %}
    {# … #}
    <div class="example-wrapper">
        <h1>{{ action }} d'auteur</h1>
        {# … #}

Vous pouvez même modifier la redirection de votre controller  Admin\AuthorController::new  pour renvoyer sur notre nouvelle page  show  , et y ajouter un bouton d’édition :

<?php
// src/Controller/Admin/AuthorController.php
if ($form->isSubmitted() && $form->isValid()) {
    $manager->persist($author);
    $manager->flush();
    
    return $this->redirectToRoute('app_admin_author_show', ['id' => $author->getId()]);
}
{# dans templates/admin/author/show.html.twig #}
{# … #}
<a href="{{ path('app_admin_author_index') }}" class="btn btn-primary">Retour</a>
<a href="{{ path('app_admin_author_edit', {id: author.id}) }}" class="btn btn-warning">Éditer</a>

Il va maintenant être temps de répliquer tout cela pour toutes nos entités. Vous pouvez copier-coller le code relatif à Author pour créer les pages Editor et Book.

À vous de jouer

Contexte

C'est maintenant le moment d'implémenter le cœur de l'application, le besoin principal qui a été exprimé par Amélie : les pages de listes et de détails ! Nous avons tout ce dont nous avons besoin, nous savons comment enregistrer et récupérer des données, il ne nous reste plus qu'à mettre le tout en œuvre.

Consignes

Vous allez implémenter le HTML du catalogue public ainsi que la page de détail d'un livre. Pour cela, vous aurez besoin :

  • d'ajouter une nouvelle classe de controller (appelez-la  BookController  ). Cette classe contiendra : 

    • une méthode controller appelée  index  sur laquelle on retrouvera le listing paginé des livres enregistrés en base de données,

    • une méthode controller appelée  show  qui affichera les détails d'un livre ;

  • des templates pour ces deux pages, qui seront construits comme suit :

    • la page de listing devra se baser sur ce thème Bootstrap gratuit. N'utilisez que la  section  centrale, celle qui contient la liste. Utilisez les conditions et boucles de Twig pour afficher vos listes de livres,

    • la page de détail d'un livre devra se baser sur ce thème Bootstrap gratuit. Ici encore, vous n'utiliserez que la  section  qui contient les détails du produit.

      • Vous afficherez les informations suivantes : ISBN, titre du livre, auteur(s), éditeur, nombre de pages et date d'édition, statut. N’hésitez pas à vous inspirer des captures d’écran.

      • Les boutons de commande en bas du template Bootstrap seront remplacés par un bouton “Retour à la liste” qui ramène au catalogue.

Vérifiez votre travail à l'aide de cet exemple de corrigé

Voici deux captures d'écran du résultat attendu :

Premier affichage du catalogue
Premier affichage du catalogue
Second affichage du catalogue
Second affichage du catalogue

En résumé

  • Les Repositories sont notre point d’entrée pour les lectures en base de données.

  • Il en existe généralement un par entité de notre application.

  • Ils disposent de 5 méthodes de base pour lire notre base de données :  find  ,  findOneBy  ,  findBy  ,  findAll   et  count  .

  • Vous pouvez rajouter des méthodes personnalisées pour vos besoins spécifiques.

  • Ces méthodes spécifiques utiliseront le plus souvent le QueryBuilder pour construire vos requêtes en base de données.

Et maintenant, voyons comment nous pouvons sécuriser notre application avec une bonne gestion des utilisateurs ! Mais avant cela, testez vos connaissances dans le quiz finalisant cette partie !

Exemple de certificat de réussite
Exemple de certificat de réussite