Pas 12: Ascultarea evenimentelor

5.0 version
Maintained

Ascultarea evenimentelor

Șablonului curent îi lipsește un antet de navigare pentru a reveni la pagina principală sau pentru a trece de la o conferință la alta.

Adăugarea unui antet

Elementele necesare de afișat pe toate paginile web, precum un antet, ar trebui să facă parte din șablonul principal:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -6,6 +6,15 @@
         {% block stylesheets %}{% endblock %}
     </head>
     <body>
+        <header>
+            <h1><a href="{{ path('homepage') }}">Guestbook</a></h1>
+            <ul>
+            {% for conference in conferences %}
+                <li><a href="{{ path('conference', { id: conference.id }) }}">{{ conference }}</a></li>
+            {% endfor %}
+            </ul>
+            <hr />
+        </header>
         {% block body %}{% endblock %}
         {% block javascripts %}{% endblock %}
     </body>

Adăugarea acestui cod la șablon înseamnă că toate șabloanele care îl extind trebuie să definească o variabilă conferences, care trebuie creată și transmisă din controlerele corespunzătoare.

Deoarece avem doar două controlere, putem face următoarele:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -32,9 +32,10 @@ class ConferenceController extends AbstractController
     /**
      * @Route("/conference/{slug}", name="conference")
      */
-    public function show(Conference $conference, CommentRepository $commentRepository)
+    public function show(Conference $conference, CommentRepository $commentRepository, ConferenceRepository $conferenceRepository)
     {
         return new Response($this->twig->render('conference/show.html.twig', [
+            'conferences' => $conferenceRepository->findAll(),
             'conference' => $conference,
             'comments' => $commentRepository->findBy(['conference' => $conference], ['createdAt' => 'DESC']),
         ]));

Imaginează-ți că trebuie să actualizezi zeci de controlere. Și va trebui să faci la fel pentru toate cele noi. Acest lucru nu este tocmai practic. Trebuie să existe o metodă mai bună.

Twig are noțiunea de variabile globale. O variabilă globală este disponibilă în toate șabloanele redate. Poți să le definești într-un fișier de configurare, dar funcționează numai pentru valori statice. Pentru a adăuga toate conferințele ca o variabilă globală Twig, vom crea o clasă de tip Listener.

Descoperirea evenimentelor Symfony

Symfony conține o componentă numită Event Dispatcher. Un dispatcher expediază (dispatch) anumite evenimente (events) în momente specifice pe care clasele Listener le pot asculta. Clasele Listener sunt cârlige din cadrul componentelor interne ale framework-ului.

De exemplu, unele evenimente îți permit să interacționezi cu ciclul de viață al solicitărilor HTTP. În timpul procesării unei solicitări, dispecerul expediază evenimente când a fost creată o solicitare, când un controler este pe punctul de a fi executat, când un răspuns este gata de trimis către browser sau când a fost aruncată o excepție. O clasă Listener poate asculta unul sau mai multe evenimente și poate executa unele acțiuni logice bazate pe contextul evenimentului.

Evenimentele sunt puncte de extensie bine definite care fac framework-ul mai generic și extensibil. Multe componente Symfony precum Security, Messenger, Workflow sau Mailer le folosesc pe scară largă.

Un alt exemplu de clase Listener și evenimente încorporate în Symfony este ciclul de viață al unei comenzi: poți crea o clasă Listener pentru a executa codul tău înainte ca orice comandă să fie executată.

Orice pachet bundle poate avea propriile evenimente pentru a face codul extensibil.

Pentru a evita crearea unui fișier de configurare care descrie explicit evenimentele pe care o clasă Listener le va asculta, poți crea o clasă de tip Subscriber. Un Subscriber este un Listener cu o metodă statică getSubscribeEvents() care returnează configurația sa. Aceasta permite abonaților să fie înregistrați automat în dispecerul Symfony.

Implementarea unui Subscriber

Cunoști deja pașii, utilizează MakerBundle pentru a genera un Subscriber:

1
$ symfony console make:subscriber TwigEventSubscriber

Comanda te va întreaba ce eveniment dorești să asculți. Alege evenimentul Symfony\Component\HttpKernel\Event\ControllerEvent, care este expediat chiar înainte de apelarea controlerului. Este cel mai bun moment pentru a injecta variabila globală conferences, astfel încât Twig va avea acces la ea atunci când controlerul va reda șablonul. Actualizeză Subscriber-ul după cum urmează:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--- a/src/EventSubscriber/TwigEventSubscriber.php
+++ b/src/EventSubscriber/TwigEventSubscriber.php
@@ -2,14 +2,25 @@

 namespace App\EventSubscriber;

+use App\Repository\ConferenceRepository;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Event\ControllerEvent;
+use Twig\Environment;

 class TwigEventSubscriber implements EventSubscriberInterface
 {
+    private $twig;
+    private $conferenceRepository;
+
+    public function __construct(Environment $twig, ConferenceRepository $conferenceRepository)
+    {
+        $this->twig = $twig;
+        $this->conferenceRepository = $conferenceRepository;
+    }
+
     public function onControllerEvent(ControllerEvent $event)
     {
-        // ...
+        $this->twig->addGlobal('conferences', $this->conferenceRepository->findAll());
     }

     public static function getSubscribedEvents()

Acum poți adăuga câte controlere dorești: variabila conferences va fi întotdeauna disponibilă în Twig.

Notă

Vom vorbi despre o alternativă mult mai bună din punct de vedere al performanței într-un pas ulterior.

Sortarea conferințelor în funcție de an și oraș

Ordonarea conferințelor în funcție de an poate facilita navigarea. Am putea crea o metodă personalizată pentru a prelua și sorta toate conferințele, dar în schimb, vom trece la suprascrierea metodei findAll() pentru a fi siguri că sortarea se aplică peste tot:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
--- a/src/Repository/ConferenceRepository.php
+++ b/src/Repository/ConferenceRepository.php
@@ -19,6 +19,11 @@ class ConferenceRepository extends ServiceEntityRepository
         parent::__construct($registry, Conference::class);
     }

+    public function findAll()
+    {
+        return $this->findBy([], ['year' => 'ASC', 'city' => 'ASC']);
+    }
+
     // /**
     //  * @return Conference[] Returns an array of Conference objects
     //  */

La sfârșitul acestui pas, site-ul web ar trebui să arate astfel:


  • « Previous Pas 11: Ramificarea Codului
  • Next » Pas 13: Gestionarea ciclului de viață al obiectelor Doctrine

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.