26/03/2023

4. Repository

Lu 287 fois Licence Creative Commons

Maintenant que la table des événements contient des données, il faut les récupérer à l'aide du EventRepository.

Page agenda

Créer un controlleur EventController avec une route event_agenda accessible sur /event/agenda.

Note: aidez-vous de la commande make:controller pour gagner du temps.

Pour toutes les routes dans ce controlleur, les noms commencerons par event_ et les chemins par /event. Pour ne pas se répeter, on peut ajouter un attribut Route au niveau de la classe:

#[Route('/event', name: 'event_')]
class EventController extends AbstractController
{
    #[Route('/agenda', name: 'agenda')]
    public function agenda(): Response
    {
        // ...
    }
}

Pour récupérer les événements il faut utiliser la classe App\Repository\EventRepository. Grâce à l'autowiring il suffit d'ajouter un argument à la méthode:

public function agenda(EventRepository $eventRepository): Response

Rappel: l'autowiring permet la récupération de services depuis le constructeur. Les controlleurs sont les seuls à le faire autrement.

Les repository possèdent 4 méthodes par défaut:

  • find(): retourne 1 entité ou null en cherchant par son identifiant
  • findOneBy(): retourne 1 entité ou null selon des critères
  • findAll(): retourne toutes les entités
  • findBy(): retourne plusieurs entités selon des critères

Nous souhaitons par défaut récupérer les événements du jour, mais aucune de ces fonctions ne pourra nous servir puisque la propriété startAt contient aussi l'heure. Cet appel ne retournerait rien car il chercherait précisément les événements dont startAt correspond à l'instant présent:

$eventRepository->findBy([
    'startAt' => new \DateTime(),
]);

Le Query Builder

Lorsque les méthodes de base ne suffisent pas, il faut en rajouter. Parmi les solutions fournies par Doctrine se trouve le Query Builder: un constructeur de requêtes.
Ouvrez /src/Repository/EventRepository.php et ajoutez une nouvelle méthode:

    /**
     * Retourne les événements qui commencent sur une journée précise
     * @return Event[]
     */
    public function findForDay(\DateTimeInterface $date): array
    {
        
    }

À l'intérieur, commencer par générer les heures de début et fin de journée:

$start = (clone $date)->setTime(0, 0);
$end = (clone $date)->setTime(23, 59);

Puis instancier le Query Builder:

// "e" sert d'alias pour l'entité courante Event
return $this->createQueryBuilder('e')

On ajoute une clause WHERE avec des paramètres pour obtenir les événements du jour:

// ...
->where('e.startAt BETWEEN :start AND :end')
->setParameters([
    'start' => $start,
    'end' => $end,
])

Note: la chaîne passée à where() ressemble à du SQL mais il s'agit de DQL (Doctrine Query Language). Il n'est donc pas possible d'utiliser des fonctions ou syntaxes spécifiques au SGBD utilisé.
Doctrine va automatiquement convertir les objets DateTime pour créer la requête SQL.

Et une clause ORDER BY pour trier chronologiquement:

->orderBy('e.startAt', 'ASC')

On termine par la récupération des résultats:

$start = (clone $date)->setTime(0, 0);
$end = (clone $date)->setTime(23, 59);

return $this->createQueryBuilder('e')
    ->where('e.startAt BETWEEN :start AND :end')
    ->setParameters([
        'start' => $start,
        'end' => $end,
    ])
    ->orderBy('e.startAt', 'ASC')
    ->getQuery()
    ->getResult()
;

De retour dans EventController::agenda, appeler cette nouvelle méthode et utiliser dd() pour en voir le résultat:

$events = $eventRepository->findForDay(new \DateTime('today'));
dd($events);

Si le tableau est vide, c'est qu'il n'y a peut-être aucun événement à la date du jour. Essayez de recharger les fixtures ou réduisez l'amplitude de startAt dans /fixtures/event.yaml.