Contrôleurs et entités

Contrôleurs et entités

1 mai 2021

Posséder des entités c'est bien ?, les utiliser c'est mieux ! Le but de cet article est justement de voir comment les utiliser depuis un contrôleur.

Les entités vont être très utiles pour gérer nos données ! En mode MVC, c'est le contrôleur qui effectue la demande au modèle et réceptionne les résultats avant de les traiter et de les transférer à la vue si besoin.

Pour nous, le modèle est composé de l'entité et son repository. Nous n'allons pas voir comment créer des requêtes DQL, ce sera l'objet d'un autre article. Symfony nous fournit les outils nécessaires pour insérer, mettre à jour, supprimer et lire des données.

Petit rappel : à la création d'une entité, un repository est généré et lié. L'entité correspondant à la structure de votre table SQL et le repository contient des méthodes permettant de communiquer avec la base de données. Pour être plus clair, le repository ne communiquera pas avec toutes les tables de votre base de données, mais seulement avec la table auquel il est relié, par exemple :

L'entité "Agences.php" possède son propre repository "AgencesRepository.php". Donc il travaillera seulement avec la table "agences".

Insérer des données

Le principe de départ n'est pas différent de la POO habituelle. N'oubliez pas que l'entité contient des getters et setters.

$agence = new Agences();
$agence->setName('Nom de mon agence');
$agence->setAddress('98 avenue par là');
$agence->setCreatedAt(new Datetime());

Évidemment, l'ID n'est pas précisé, car il est en "auto incrément", donc on ne s'en préoccupe pas.

Pour terminer l'insertion, nous utiliserons le manager de Doctrine, puis nous persisterons les données et enfin nous effectuerons un "flush".

Allons-y :

1. Récupération du manager de Doctrine :

$em = $this->getDoctrine()->getManager();
"$em" est le nom de la variable que j'ai choisie, libre à vous de changer.

2. Nous persistons les données :

$em->persist($agence);

Persister les données ? Cela permet de sauvegarder des données afin de pouvoir les retrouver en tout temps. Pour le moment, "$agence" est juste qu'un objet et est transmis à Doctrine, mais ce n'est pas pour autant que les données se retrouvent en base de données.

3. Enregistrement des données en BDD :

$em->flush();

Maintenant, l'insertion en base de données s'est bien déroulée.
Voilà à quoi ressemble ma méthode pour le moment :

/**
 * @Route("/new", name="new")
 */
public function new()
{
    $em = $this->getDoctrine()->getManager();

    $agence = new Agences();
    $agence->setName('Nom de mon agence');
    $agence->setAddress('98 avenue par là');
    $agence->setCreatedAt(new Datetime());

    $em->persist($agence);
    $em->flush();

    return $this->render('home/index.html.twig');
}

À vrai dire ce n'est pas tout à fait fini. Mon entité est reliée en "ManyToOne" à une autre entité "Villes.php". Il me manque cette donnée pour compléter l'insertion de mon agence. On pourrait penser qu'il suffit de passer un ID et le tour est joué ! Et... bien... non... ?‍♂️

Le setter de notre relation attend un objet ! Il va falloir d'abord récupérer la ville que l'on souhaite relier à notre agence et enfin passer l'objet à notre setter pour compléter l'insertion.

// Je ne recherche qu'une seule ville, donc findOneBy()
$ville = $this->getDoctrine()->getRepository(Villes::class)->findOneByName('Troyes');

// ...

$agence->setVille($ville);

// ...

Complétons notre code avec ces nouveaux paramètres :

/**
 * @Route("/new", name="new")
 */
public function new()
{
    $em = $this->getDoctrine()->getManager();
    $ville = $this->getDoctrine()->getRepository(Villes::class)->findOneByName('Troyes');

    $agence = new Agences();
    $agence->setName('Nom de mon agence');
    $agence->setAddress('98 avenue par là');
    $agence->setCreatedAt(new Datetime());
    $agence->setVille($ville);

    $em->persist($agence);
    $em->flush();

    return $this->render('home/index.html.twig');
}

Toudoum ! Euh non... Tadam !! ?

Mettre à jour une donnée

La mise à jour n'est pas si différente de l'insertion. Il faut dans un premier temps récupérer les données à modifier. Ensuite, il faut modifier les données voulues, persister et "flusher".

/**
 * @Route("/update", name="update")
 */
public function update()
{
    $em = $this->getDoctrine()->getManager();

    $agence = $this->getDoctrine()->getRepository(Villes::class)->find(2);
    $agence->setName('Nouveau nom de mon agence');

    $em->persist($agence);
    $em->flush();

    return $this->render('home/index.html.twig');
}

J'ai récupéré une agence via son ID, en l'occurrence l'ID numéro 2, pour modifier son nom. Vous pouvez le constater par vous-même, la différence entre l'insertion et la mise à jour reste minime.

Suppression d'une donnée

La suppression est encore plus rapide que l'ajout et la modification. Vous récupérez la donnée à supprimer, vous la passez à Doctrine et vous validez la suppression.

/**
 * @Route("/remove", name="remove")
 */
public function remove()
{
    $em = $this->getDoctrine()->getManager();

    $agence = $this->getDoctrine()->getRepository(Villes::class)->find(2);

    $em->remove($agence);
    $em->flush();

    return $this->render('home/index.html.twig');
}

Si je devais comparer avec l'une des autres méthodes, on se rapproche de la modification d'informations. Je récupère l'agence à supprimer via son ID. Cette fois-ci je n'utilise pas persist(), mais remove().

Lire des données

Pour lire des données, depuis un contrôleur, nous allons utiliser une méthode de Doctrine en lui passant quelques paramètres. Ce qui est bien avec Symfony, c'est que l'on a quatre méthodes toutes prêtes et qui sont généralement largement suffisantes pour lire nos données. Voyons-les ensemble.

findAll()

Je crois que le nom est explicite. Cette méthode nous permet de retourner tous les résultats de la table interrogée. Pour l'utiliser, nous devons faire appel au repository de l'entité concernée grâce au code suivant :

$agences = $this->getDoctrine()->getRepository(Agences::class)->findAll();

Dans mon cas, j'ai passé en paramètre du getRepository() mon entité "Agences" car je souhaite avoir la totalité des résultats contenus dans la table "agences".

Remarque : pas besoin de passer directement le repository dans la méthode, mais bien l'entité avec laquelle vous souhaitez travailler !

À partir de là, "$agences" est une collection contenant la totalité des données trouvées.

findBy()

Cette méthode permet de récupérer zéro, une ou plusieurs données selon un ou plusieurs critères.
En SQL, son équivalent est WHERE.

Elle peut s'utiliser de deux manières différentes, mais est tout aussi pratique. La première façon de l'utiliser est de modifier le nom de la méthode en passant directement le nom de la propriété avec laquelle on veut effectuer notre recherche. Bon, un exemple sera plus parlant. ?

Mon entité "Agences.php" possède cinq propriétés :

  • id
  • name
  • address
  • created_at
  • city (ma relation de table)

Admettons que je veuille toutes les agences ayant pour nom "Agence et cie" :

$agences = $this->getDoctrine()->getRepository(Agences::class)->findByName('Agence et cie');

Pas mal non ? ? Mon findBy() s'est transformée en findByName(). Il suffit maintenant d'adapter selon ses besoins. Alors ok, c'est sympa tout ça, mais si je souhaite trouver plusieurs agences ayant ce nom qui ont une adresse précise, cette manière de faire ne convient pas. C'est là qu'intervient la seconde façon de l'utiliser.

findBy() attend en premier paramètre un tableau associatif ayant pour clé le nom de la propriété dans laquelle vous passer une valeur à chercher. Exemple :

$agences = $this->getDoctrine()->getRepository(Agences::class)->findBy(['name' => 'Agence et cie', 'address' => '3 rue des lilas');

Elle prend aussi d'autres paramètres comme orderBy, limit et offset.

findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)

find()

Cette méthode trouve une donnée via son ID. Attention, elle ne retourne qu'une seule donnée, si celle-ci est trouvée bien entendu.

$agence = $this->getDoctrine()->getRepository(Agences::class)->find(9);

Seul l'ID peut être utilisé avec cette méthode ; vous ne pouvez pas l'utiliser avec d'autres informations, comme le titre ou le nom.

findOneBy()

Je ne vais pas entrer dans le détail de celle-ci, car son fonctionnement est exactement le même que findBy() ! Il faut prendre en compte quelques différences qui sont :

  • Ne retourne qu'un seul résultat, si trouvé.
  • limit et offset n'existent pas (normal, un seul résultat est renvoyé).
  • orderBy existe en second paramètre.

Allez, je vous mets les deux façons de l'utiliser. ?

$agence = $this->getDoctrine()->getRepository(Agences::class)->findOneByName('Agence et cie');
$agence = $this->getDoctrine()->getRepository(Agences::class)->findOneBy(['name' => 'Agence et cie', 'address' => '3 rue des lilas');

Afficher les données

Ok, c'est super tout ça, mais comment je les affiche à ma vue toutes ces données ? Très bonne question à laquelle je m'empresse d'y répondre !

findAll() et findBy()

Commençons avec findAll() et findBy(). Ces deux méthodes nous retournent zéro ou plusieurs données, donc nous devons utiliser une boucle pour les lires.

{% for agence in agences %}
    <div>
        <h2>{{ agence.name }}</h2>
        <p>{{ agence.address }}</p>
        <p>Créer le {{ agence.createdAt | date('d.m.Y') }}</p>
        <p>{{ agence.ville.name }}</p>
    </div>
{% else %}
    <p>Aucune agence</p>
{% endfor %}

"agence" est un objet et pour lire en mode "objet" dans Twig, on utilise un point, là où en PHP on utiliserait une flèche ->.

Pour lire la date, j'ai dû appliquer un filtre ; sans cela, une erreur me serait retournée.

Pour le nom de la ville, j'utilise le système de relations de Symfony. Mon entité "Agences.php" est reliée à mon entité "Villes.php", d'où je peux récupérer le nom de la ville liée à l'agence. C'est pourquoi je me retrouve à chaîner plusieurs propriétés : agence (mon objet), ville (propriété dans mon entité Agence) et name (propriété dans mon entité Villes).

find() et findOneBy()

Ces deux méthodes ne retournent qu'un seul résultat, donc il n'y a pas besoin de boucle.

<div>
    <h2>{{ agence.name }}</h2>
    <p>{{ agence.address }}</p>
    <p>Créer le {{ agence.createdAt | date('d.m.Y') }}</p>
    <p>{{ agence.ville.name }}</p>
</div>

Si jamais votre requête ne vous retourne aucun résultat, l'affichage du dessus retournera une erreur. Pour pallier cette éventualité, appliquons un if :

{% if agence %}
    <div>
        <h2>{{ agence.name }}</h2>
        <p>{{ agence.address }}</p>
        <p>Créer le {{ agence.createdAt | date('d.m.Y') }}</p>
        <p>{{ agence.ville.name }}</p>
    </div>
{% else %}
    <p>Aucune agence</p>
{% endif %}