Schritt 12: Mit Events arbeiten

5.0 version
Maintained

Mit Events arbeiten

Dem aktuellen Layout fehlt eine Navigation, um zur Homepage zurückzukehren oder von einer Konferenz zur nächsten zu wechseln.

Einen Website-Header hinzufügen

Alles, was auf allen Webseiten angezeigt werden soll, wie z. B. ein Header, sollte Teil des Haupt-Basislayouts sein:

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>

Das Hinzufügen dieses Codes zum Layout bedeutet, dass alle Templates, die es erweitern, eine conferences-Variable definieren müssen, die von ihren Controllern erstellt und übergeben werden muss.

Da wir nur zwei Controller haben, könntest Du folgendes tun:

 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']),
         ]));

Stelle Dir vor, Du müsstest Dutzende von Controllern aktualisieren. Und das Gleiche bei allen neuen Controllern tun. Das ist nicht besonders praktisch. Es muss einen besseren Weg geben.

Twig bietet die Möglichkeit globale Variablen zu definieren. Eine globale Variable ist in allen gerenderten Vorlagen verfügbar. Du kannst sie in einer Konfigurationsdatei definieren, aber das funktioniert nur bei statischen Werten. Um alle Konferenzen als globale Variable zu Twig hinzuzufügen, werden wir einen Listener erstellen.

Symfony Events entdecken

Symfony ist mit einer Event Dispatcher Komponente ausgestattet. Ein Dispatcher verteilt bestimmte Events zu bestimmten Zeiten, die ein Listener abonnieren kann. Listener sind Hooks im Inneren des Frameworks.

Einige Events erlauben es Dir beispielsweise, mit dem Lifecycle von HTTP-Requests zu interagieren. Während der Bearbeitung eines Requests sendet der Dispatcher Events, sobald ein Request erstellt wurde, ein Controller aufgerufen werden soll, eine Response zum Senden bereit ist oder eine Exception geworfen wurde. Ein Listener kann auf ein oder mehrere Events reagieren und Logik basierend auf dem Eventkontext ausführen.

Events sind klar definierte Erweiterungspunkte, die das Framework generischer und erweiterbarer machen. Viele Symfony-Komponenten wie Security, Messenger, Workflow oder Mailer verwenden sie häufig.

Ein weiteres eingebautes Beispiel für Events und Listener ist der Lifecycle eines Befehls: Du kannst einen Listener erstellen, um Code vor jedem Befehl auszuführen.

Jedes Paket oder Bundle kann auch eigene Events auslösen, um seinen Code erweiterbar zu machen.

Damit du nicht alle Events und Listener in einer Konfigurationsdatei beschreiben musst, kannst du einen Subscriber erstellen. Ein Subscriber ist ein Listener mit einer statischen getSubscribedEvents()-Methode, die seine Konfiguration zurückgibt. Dadurch können Subscriber automatisch im Symfony Dispatcher registriert werden und Events abonnieren.

Einen Subscriber implementieren

Du kennst das Lied bestimmt schon auswendig, verwende das Maker-Bundle, um einen Subscriber zu generieren:

1
$ symfony console make:subscriber TwigEventSubscriber

Der Befehl fragt Dich, über welches Event Du informiert werden möchtest. Wähle das Symfony\Component\HttpKernel\Event\ControllerEvent-Event, welches kurz vor dem Aufruf eines Controllers ausgelöst wird. Dies ist der beste Zeitpunkt, die globale conferences-Variable einzuspeisen, damit Twig Zugriff darauf hat, wenn der Controller das Template rendert. Passe Deinen Subscriber wie folgt an:

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()

Jetzt kannst Du beliebig viele Controller hinzufügen: Die conferences-Variable wird in Twig immer verfügbar sein.

Bemerkung

Wir werden in einem späteren Schritt über eine viel bessere Alternative in Bezug auf Performance sprechen.

Konferenzen nach Jahr und Stadt sortieren

Eine nach Jahren sortierte Konferenzliste kann das Durchsuchen erleichtern. Wir könnten eine spezifische Methode erstellen, um alle Konferenzen abzurufen und zu sortieren, stattdessen werden wir jedoch die Standardimplementierung der findAll()-Methode überschreiben. Auf diese Weise stellen wir sicher, dass die Sortierung überall angewendet wird:

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
     //  */

Nach diesem Schritt sollte die Seite wie folgt aussehen:


  • « Previous Schritt 11: Den Code branchen
  • Next » Schritt 13: Den Lifecycle von Doctrine-Objekten verwalten

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