Cours - Symfony - Chapitre 06 - Afficher la liste des contacts reçus

SOMMAIRE



Introduction



Dans le précédent chapitre, nous avons enregistré dans notre base de données les prises de contact. Pour cela nous avons créé une entité « Contact » et par l’intermédiaire de l’ORM « Doctrine », la table « Contact » s’est créée dans MariaDB.

Nous allons maintenant créer une nouvelle page afin d’afficher toutes les valeurs de la table « Contact ».

Mise en place d’une nouvelle page

Vous savez mettre en place certaines choses pour réaliser l’objectif de ce chapitre. Ce qui va suivre est donc un exercice, mais pas de panique, vous trouverez la correction juste en dessous.

Exercice



En vous appuyant sur le chapitre 1 créez un contrôleur que vous nommerez « Contact ».

Vous devez voir apparaitre un nouveau fichier dans « src/Controller » :





Dans ce contrôleur, modifiez la fonction créée par défaut en vous appuyant sur le chapitre 2 et sur les données suivantes :

Vous devrez obtenir :

  • une fonction « listeContacts,

  • une url « /liste-contacts »

  • une route « liste-contacts »

  • un fichier TWIG « liste-contact.html.twig





Voici une capture d’écran du résultat attendu avec une URL qui ressemble à celle-ci :

https://s3-4400.nuage-peda.fr/share/public/liste-contacts







Correction 

En se mettant sur le compte login tapez la commande :

php bin/console make:controller Contact

Contenu du ficher « src/ContactController.php » :

<?php

namespace App\Controller;


use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ContactController extends AbstractController
{
    #[Route('/liste-contacts', name: 'liste-contacts')]
    public function listeContacts(): Response
    {
        return $this->render('contact/liste-contacts.html.twig', [
           
        ]);
    }
}

Contenu du fichier « templates/contact/liste-contacts.html.twig » :

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

{% block title %}{{parent()}}Liste des contacts{% endblock %}

{% block body %}
    <h1 class="text-center text-primary mt-4 pt-4 display-1 fw-bold"> Liste des contacts</h1>
{% endblock %}



Récupération des données dans le contrôleur

Souvenez-vous, nous sommes dans une architecture MVC (Modèle Vue Contrôleur). C’est donc le contrôleur qui va faire appel au Modèle afin d’obtenir les données attendues pour ensuite les donner à la Vue pour les afficher.

Première étape :

Ajoutez le « use » pour indique à votre contrôleur le chemin vers l’entité « Contact ».

Ajoutez le « use Doctrine\ORM\EntityManagerInterface; » afin d'utiliser le manager d'entités.

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\Contact;
use Doctrine\ORM\EntityManagerInterface;



Deuxième étape :



Demandez à « Doctrine » de nous fournir le « repository » de l’entité « Contact ». Ensuite nous utiliserons la méthode qui permet de récupérer une liste des contacts présents dans la table « Contact ». Petit rappel du chapitre précédent :

 Méthode qui retourne la valeur nulle ou un contact dont l’identifiant sera passé en paramètre.
@method Contact|null find($id, $lockMode = null, $lockVersion = null)

Méthode qui retourne la valeur nulle ou un contact par rapport à un ensemble de critères que nous pouvons passer en paramètre.
@method Contact|null findOneBy(array $criteria, array $orderBy = null)

Méthode qui retourne un tableau comprenant tous les contacts présents dans la table « Contact ».
@method Contact[] findAll()

Méthode qui retourne un tableau comprenant tous les contacts présents dans la table « Contacts » qui vérifient des critères passés en paramètre.
@method Contact[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)

Mettons en place la récupération des contacts dans le contrôleur :

 #[Route('/liste-contacts', name: 'liste-contacts')]
    public function listeContacts(EntityManagerInterface $entityManagerInterface): Response
    {
        $repoContact = $entityManagerInterface->getRepository(Contact::class);
        $contacts = $repoContact->findAll();
        return $this->render('contact/liste-contacts.html.twig', [
           
        ]);
    }

Remarque : « $contacts » contient tous les contacts de la base de données.

Le contrôleur doit maintenant donner les contacts à la vue :

#[Route('/liste-contacts', name: 'liste-contacts')]
    public function listeContacts(EntityManagerInterface $entityManagerInterface): Response
    {
        $repoContact = $entityManagerInterface->getRepository(Contact::class);
        $contacts = $repoContact->findAll();
        return $this->render('contact/liste-contacts.html.twig', [
           'contacts' => $contacts
        ]);
    }

Affichage des contacts dans la vue :

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

{% block title %}
	{{parent()}}Liste des contacts
{% endblock %}

{% block body %}
	<div class="container-fluid">
		<h1 class="text-center text-primary mt-4 pt-4 display-1 fw-bold">
			Liste des contacts</h1>


		<div class="row justify-content-center">
			<div class="col-12 col-md-8 bg-white p-4 m-0 text-primary">
				<div class="table-responsive">
					<table class="table table-hover">
						<thead>
							<tr class="fw-bold text-primary">
								<th scope="col">Nom</th>
								<th scope="col">Sujet</th>
                                <th scope="col">Email</th>
							</tr>
						</thead>
						<tbody>
							{% for contact in contacts %}
								<tr class="{{ cycle(['table-primary', 'table-secondary'], loop.index0) }}">
									<td>{{ contact.nom | capitalize }}</td>
									<td>{{ contact.sujet | capitalize }}</td>
                                    <td>{{ contact.email | lower }}</td>
								</tr>
							{% endfor %}
						</tbody>
					</table>
				</div>
			</div>
		</div>
	</div>

{% endblock %}

Commentaires :

{% for contact in contacts %} // le for permet de parcourir tous les contacts. Au départ, il met le premier contact dans la valeur « contact ». Il traite toutes les instructions jusqu’au

{% endfor %}, puis, il recommence le traitement en prenant le 2e contact et ainsi de suite.

<tr class="{{ cycle(['table-primary', 'table-secondary'], loop.index0) }}"> // permet d’alterner la couleur de la ligne du tableau. La classe utilisée pour afficher le premier contact est ‘table-primary’.

La classe utilisée pour afficher le deuxième contact est ‘table-secondary’. Pour afficher le 3e contact, il utilisera la classe ‘table-primary’, etc.

‘cycle’ est une fonction TWIG qui parcoure un tableau, ‘loop.index0’ est l’itération courante qui commence à 0.

Les doubles accolades {{ }} permettent d’afficher le contenu d’une variable.

{{ contact.nom | capitalize }} // permet d’afficher le nom stocké dans la variable contact.

Le ‘| ‘ permet d’appliquer des filtres TWIG sur la donnée.

« Capitalize » est une fonction TWIG permettant de formater une chaine de caractères. La première lettre sera une majuscule et le reste sera en minuscule.

« lower » est une fonction TWIG qui permet de mettre une chaine de caractères en minuscule.

<div class="container-fluid"> // est une div de Bootstrap utile lorsque vous utilisez son système de grilles.

<div class="row justify-content-center"> // est une ligne d’une grille Bootstrap dont le contenu sera centré.

<div class="col-12 col-md-8 bg-white p-4 m-0 text-primary"> // col-12 signifie que la colonne va prendre toute la place disponible sur l’écran s’il s’agit d’un inférieur à 576 pixels. Bootstrap considère que l’écran fait 12 colonnes.

Nous voyons « col-md-8 », cela signifie qu’à partir de 768 pixels, la colonne va prendre 8 colonnes de l’écran, ce qui réduit la taille du tableau.

Pour en savoir plus, voici le lien vers la page de Bootstrap Grid

<div class="table-responsive"> // nous encadrons le tableau avec cette classe, ainsi, si le tableau est plus grand que l’écran, un ascenseur horizontal se créé.

Voici le résultat :





Améliorations :

Il serait intéressant d’afficher les contacts du plus récent au plus éloigné. De même, la date d’envoi serait un bon indicateur.

Modification de l’entité Contact

Pour chaque prise de contact, nous devons conserver la date d’envoi. Nous allons donc ajouter cette donnée dans l’entité « Contact ».

php bin/console make:entity Contact

Ajoutez la propriété « dateEnvoi » de type « datetime ». Nous ne souhaitons pas que cette valeur soit nulle.

Préparez la migration :

php bin/console make:migration

Mettez à jour la base de données :

php bin/console doctrine:migrations:migrate

Modification du contrôleur « contact » dans « BaseController »

 #[Route('/contact', name: 'contact')]
    public function contact(Request $request, MailerInterface $mailer, EntityManagerInterface $entityManagerInterface): Response
    {
        $contact = new Contact();
        $form = $this->createForm(ContactType::class, $contact);

        if($request->isMethod('POST')){
            $form->handleRequest($request);
            if ($form->isSubmitted()&&$form->isValid()){   
                $email = (new TemplatedEmail())
                ->from($contact->getEmail())
                ->to('reply@nuage-pedagogique.fr')
                ->subject($contact->getSujet())
                ->htmlTemplate('emails/email.html.twig')
                ->context([
                    'nom'=> $contact->getNom(),
                    'sujet'=> $contact->getSujet(),
                    'message'=> $contact->getMessage()
                ]);
                $contact->setDateEnvoi(new \Datetime());
                $entityManagerInterface->persist($contact);
                $entityManagerInterface->flush();
              
                $mailer->send($email);
                $this->addFlash('notice','Message envoyé');
                return $this->redirectToRoute('contact');
            }
        }

        return $this->render('base/contact.html.twig', [
            'form' => $form->createView()
        ]);
    }

Commentaire :

$contact->setDateEnvoi(new \Datetime()); // Nous mettons directement la date et l’heure actuelles que nous récupérons avec la classe Datetime et que nous donnons donnons directement à l’accesseur de « dateEnvoi ».

Voici l’extrait de la table « Contact » :

Affichage de l’heure dans la vue 

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

{% block title %}
	{{parent()}}Liste des contacts
{% endblock %}

{% block body %}
	<div class="container-fluid">
		<h1 class="text-center text-primary mt-4 pt-4 display-1 fw-bold">
			Liste des contacts</h1>


		<div class="row justify-content-center">
			<div class="col-12 col-md-8 bg-white p-4 m-0 text-primary">
				<div class="table-responsive">
					<table class="table table-hover">
						<thead>
							<tr class="fw-bold text-primary">
								<th scope="col">Nom</th>
								<th scope="col">Sujet</th>
                                <th scope="col">Email</th>
								<th scope="col">Date d'envoi</th>

							</tr>
						</thead>
						<tbody>
							{% for contact in contacts |sort((a, b) => b.dateEnvoi <=> a.dateEnvoi) %}
								<tr class="{{ cycle(['table-primary', 'table-secondary'], loop.index0) }}">
									<td>{{ contact.nom | capitalize }}</td>
									<td>{{ contact.sujet | capitalize }}</td>
                                    <td>{{ contact.email | lower }}</td>
									<td>{{ contact.dateEnvoi | date("d-m-Y à H:i:s") }}</td>

								</tr>
							{% endfor %}
						</tbody>
					</table>
				</div>
			</div>
		</div>
	</div>

{% endblock %}

Commentaires :

{% for contact in contacts |sort((a, b) => b.dateEnvoi <=> a.dateEnvoi) %} // Nous utilisons le filtre « sort » qui permet de trier une liste. Il prend la 1ère valeur (a) et la compare avec la 2e (b) sur la date d’envoi. Si la 1ère valeur est plus grande que la 2e, il intervertit les valeurs (b.dateEnvoi <=> a.dateEnvoi). Il poursuit le traitement en comparant les valeurs suivantes.

<td>{{ contact.dateEnvoi | date("d-m-Y à H:i:s") }}</td> // Une date ne peut pas s’afficher sans le filtre « date ». Nous pouvons lui donner en paramètre les valeurs que nous souhaitons afficher. Le numéro du jour (d), le numéro du mois (m), l’année sur 4 chiffres (Y), l’heure (H), les minutes (i) et enfin les secondes(s).

Voici le résultat :





Voir le Chapitre 7