Eine Einführung in die Neuerungen im Zend Framework 2

Früher war (fast) alles anders
Kommentare

Nach mehrjähriger Arbeit ist am 4. September 2012 die erste stabile Version vom Zend Framework 2 erschienen. Wer sich in der langen Entwicklungsphase nur sporadisch mit dem neuen ZF2 auseinandergesetzt hat, wird sich zu Beginn von den Veränderungen erschlagen fühlen. Dieser Artikel soll die wesentlichen Änderungen zusammenfassen und Hilfestellung beim Einstieg geben.

Nach einer kurzen Einführung in das ZF2-Ecosystem fahren wir mit der Installation der Skeleton Application fort. Auf Basis dieses Gerüsts gehen wir auf die neuen Konzepte des Zend Framework 2 ein. Zum Ende folgt die Installation von weiteren Modulen.

Alle Listings zum Artikel stehen bei GitHub bereit und können geklont oder als ZIP heruntergeladen werden.

Das ZF2-Ecosystem

Mit dem Erscheinen der stabilen Version 2.0.0 wurde die frameworkbegleitende Website überarbeitet, der gesamte Programmcode lässt sich auf GitHub einsehen und forken. Die Website legt einen Schwerpunkt auf das ZF2, das ZF1 kommt aber nicht zu kurz. Neben den FAQ finden sich dort Informationen zu Training, Zertifizierung und Support. Ein Einsteiger-Tutorial, das Referenzhandbuch sowie die API-Dokumentation sind ebenso zu finden wie ein ausführlicher Bereich, der die Möglichkeiten zur Installation des ZF2 zusammenfasst. Informationen, wie sich interessierte Nutzer an der Weiterentwicklung des Zend Framework beteiligen können, runden das Angebot der Website ab.

Übersichtlicher als auf der Hauptwebsite kommt die Dokumentation auf der Plattform readthedocs.org daher. Dazu noch eine kleine Warnung: das Referenzhandbuch war bei Erscheinen der 2.0.0-Version noch unvollständig. Während ich diese Zeilen schreibe, arbeitet das Entwicklerteam jedoch daran, die fehlende Dokumentation nachzureichen und unvollständige zu ergänzen.

Eine weitere spezialisierte Website beschäftigt sich mit den Installationsmöglichkeiten für das Zend Framework 2. Hier werden die Source-Pakete zum Download angeboten. Die Installation einzelner Pakete mit Pyrus (auch als PEAR Installer bekannt) wird ebenso erläutert wie die Installation mit Composer. Links zu den GitHub-Quellen sowie weitere Links helfen Einsteigern und Profis bei der Installation.

An dieser Stelle sei auch die neue ZF2-Module-Website erwähnt. Die Seite befindet sich im Aufbau und hat sich zum Ziel gesetzt, erste Anlaufstelle für die Suche nach ZF2-Modulen zu sein. Noch ist das Angebot ungeordnet und es fällt schwer, die Spreu vom Weizen zu trennen. Eine Abstimmfunktion und die Durchsuchbarkeit sollen dem Nutzer später helfen, die passenden Module für das eigene Projekt zu finden.

Für alle, die sich intensiver mit der Weiterentwicklung des Frameworks auseinandersetzen möchten, sei das ZF2 Wiki empfohlen. Dort finden sich Informationen zu Meilensteinen, neuen Proposals und RFCs (Request for Comment) sowie die IRC-Meeting-Logs zum Nachlesen.

Bereits erwähnt wurde GitHub. Wurde für das ZF1 noch SVN als V24e (Abkürzung für Versionsmanagementsoftware) verwendet, so hat das ZF2 von Beginn auf Git gesetzt. Wer sich mit Git noch nicht beschäftigt hat, findet im Wiki eine kurze Einführung für Entwickler. GitHub wiederum ist ein webbasierter Hosting-Dienst für Softwareentwicklungsprojekte, die Git einsetzen. Ein interaktives Tutorial hilft beim Einstieg in Git und GitHub.

Auf GitHub finden sich die Repositories für das Zend Framework 2. Dort kann man nicht nur die Library herunterladen, sondern auch auf die Quellen der offiziellen Website zugreifen. Außerdem befinden sich hier die Skeleton Application, zu der wir gleichkommen, sowie die Dokumentation und die Repositories der Web Services, die nicht mehr zum Kern des ZF2 gehören. Ebenfalls einen Blick sind die Repositories der ZF-Commons-Initiative wert, deren Ziel die Schaffung von hochwertigen ZF2-Modulen ist.

Installation der Skeleton Application

Zuerst ist es für manche ein Schock: Zend_Tool gibt es nicht mehr, zumindest noch nicht. Es gibt ein neues ZendTool-Repository, doch das steckt noch in den Kinderschuhen. Für das Anlegen eines neuen Projektes kann stattdessen die Skeleton Application verwendet werden. Darin ist das Gerüst einer Zend-Framework-2-Applikation enthalten, auf dem aufgebaut werden kann.

Zuerst legen wir ein Verzeichnis für unser neues Projekt an. Unter Linux könnte das Verzeichnis /home/devhost/zf2phpmagazin/ lauten. Windows-Nutzer können stattdessen D:devhostzf2phpmagazin verwenden. Im Folgenden wird nur noch auf /home/devhost/zf2phpmagazin/ verwiesen.

Wir rufen das Repository für die Skeleton Application auf. Oben links ist ein Link für den Download als ZIP-Paket enthalten. Nach dem Download entpacken und alle darin enthaltenen Dateien und Unterverzeichnisse nach /home/devhost/zf2phpmagazin kopieren. Das /public-Verzeichnis sollte in /home/devhost/zf2phpmagazin/public liegen.

Wir wechseln in der Konsole in das Verzeichnis des Projektes, um die Installation mithilfe von Composer zu beginnen. Vorher wird der Composer aufgerufen, um sich zu aktualisieren, danach wird die Installation gestartet. Dabei wird die Zend Framework Library heruntergeladen und installiert, da diese nicht Teil der Skeleton Application ist. Das Ergebnis sollte aussehen wie in Listing 1.

Um die Installation abzuschließen, muss der Webserver konfiguriert werden (um den Rahmen des Artikels nicht zu sprengen, erläutere ich dies nur für den Apache2 unter Linux). Falls das mod_rewrite-Modul noch nicht aktiviert ist, kann dies über die Konsole nachgeholt werden.

> sudo a2enmod rewrite

Danach erstellen wir die Datei für den Virtual Host und kopieren den Inhalt aus Listing 1 hinein. Als Nächstes wird der neue Host aktiviert und der Apache neu gestartet:

> sudo touch /etc/apache2/sites-available/zf2phpmagazin
> sudo gedit /etc/apache2/sites-available/zf2phpmagazin
> sudo a2ensite zf2phpmagazin
> sudo service apache2 restart

Als Letztes müssen wir unserer hosts-Datei den neuen Host mitteilen. Dafür tragen wir mit sudo gedit /etc/hosts für die IP 127.0.0.1 den neuen Host zf2phpmagazin ein.

Nun können wir im Browser das neue Projekt unter http://zf2phpmagazin/ aufrufen, das Ergebnis sollte wie in Abbildung 1 aussehen. Damit ist die Installation der Skeleton Application abgeschlossen. Es empfiehlt sich, dieses Projekt im IDE seiner Wahl (ZendStudio, PHPStorm, NetBeans etc.) anzulegen, um leichter auf die Dateien zugreifen zu können.

Abb. 1: Erster Aufruf der Skeleton Application

Applikationsstruktur

Der Aufbau eines ZF2-Projekts unterscheidet sich von dem seines Vorgängers. Es gibt folgende fünf Unterverzeichnisse:

  1.  /config/
  2. /data/
  3. /module/
  4.  /public/
  5. /vendor/

Im /config/-Verzeichnis werden alle applikationsweiten Konfigurationsdateien abgelegt. Im Zentrum steht dabei die Dateien /config/application.config.php, die Auskunft über die zu verwendenden Module enthält. Die Option config_glob_paths gibt an, welche Konfigurationsdateien in welcher Reihenfolge geladen werden sollen. Dabei sagt die Angabe config/autoload/{,*.}{global,local}.php aus, dass alle Dateien aus dem Verzeichnis /config/autoload/ geladen werden sollen, die auf „global“ oder „local“ enden, wobei die Einstellungen aus den local-Dateien jene aus den global-Dateien überschreiben können. Damit lassen sich kaskadierende Konfigurationen für Stufen wie Development, Testing und Live umsetzen, wobei in diesem Fall local für Development und global für Live steht.

Das /data/-Verzeichnis ist bereits aus dem ZF1 bekannt. Hier können nicht dauerhaft zu speichernde Daten wie Sessions, Caches, Mails oder Logs abgelegt werden.

Lag der Kern einer Anwendung im ZF1 im Verzeichnis /application/, hat diese Rolle in Version 2 das Verzeichnis /module/ übernommen. Darin befindet sich ein Modul Application, das für den reibungslosen Ablauf unserer Anwendung zuständig ist und das wir nach Bedarf anpassen können. Der Aufbau eines Moduls wird im nächsten Abschnitt erläutert.

Das /public/-Verzeichnis ist ebenfalls vom ZF1 bekannt. Hier liegen weiterhin die Grafiken, CSS- und JS-Dateien sowie unsere index.php und die .htaccess für den Apache.

Als Letzter im Bunde ersetzt /vendor/ das bekannte /library/-Verzeichnis, das im ZF1 verwendet wurde. Hier liegen unter /vendor/zendframework/ die ZF2-Dateien, die neben der Library auch die Tests, Demos, weitere Ressourcen sowie Skripte enthalten. Zudem befinden sich in /vendor/composer/ weitere Dateien, die der Composer für die Installation von Abhängigkeiten benötigt. In dem /vendor/-Verzeichnis können auch alle Module liegen, die nicht direkt für unsere Anwendung erstellt werden. Das können fertige Module oder auch die eigenen Library-Erweiterungen sein, wie wir sie aus dem ZF1 kennen.

Modulstruktur

Das Application-Modul enthält vier weitere Unterverzeichnisse sowie eine zusätzliche Datei:

  1. /config/
  2. /language/
  3. /src/
  4. /view/
  5. Module.php

Alle Konfigurationsdateien für das Modul gehören ins Verzeichnis /config/. Dort liegt momentan die Datei module.config.php, die Konfigurationen für das Application-Modul bereithält. Darin werden der Router, die Services (dazu später mehr), der Translator, die Controller des Moduls und die Moduleinstellungen für den View Manager festgelegt.

Das Verzeichnis /language/ beinhaltet die Sprachdateien für das Modul, und im /src/-Verzeichnis sollen alle Klassen abgelegt werden, die das Modul bereithält und benötigt. Hierbei ist zu beachten, dass unter /src/ der Name des Moduls wiederholt werden sollte. Bisher ist nur das Verzeichnis /src/Application/Controller/ angelegt. Hier ist Platz für Formulare, Plug-ins, Filter, Optionsklassen oder View Helper.

Im Verzeichnis /view /sind alle View- und Layout-Skripte zu finden. Neben den View-Skripten für die einzelnen Controller in /view/application/ liegen hier die Fehler-Templates und die Layoutskripte. Wer übrigens nun einen ErrorController sucht, der wird enttäuscht sein: die Funktionalitäten wurden in verschiedene Listener ausgelagert, was einen eigenen ErrorController obsolet macht.

Die Datei Module.php ist Kern eines jeden Moduls. Ein Modul kann nur erkannt und gelesen werden, wenn diese Datei vorhanden und darin die Klasse Module enthalten ist. Übrigens kann ein Modul auch nur aus einer einzelnen Module.php bestehen. Zur Erklärung schauen wir uns die Klasse in Listing 1 (Listing 3 bei GitHub) genauer an.

namespace Application;

use ZendMvcModuleRouteListener;
use ZendMvcMvcEvent;

class Module
{
  public function onBootstrap(MvcEvent $e)
  {
    $e->getApplication()->getServiceManager()->get('translator');
    $eventManager = $e->getApplication()->getEventManager();
    $moduleRouteListener = new ModuleRouteListener();
    $moduleRouteListener->attach($eventManager);
  }

  public function getConfig()
  {
    return include __DIR__ . '/config/module.config.php';
  }

  public function getAutoloaderConfig()
  {
    return array(
      'ZendLoaderStandardAutoloader' => array(
        'namespaces' => array(
          __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
        ),
      ),
    );
  }
}

Die Methode onBootstrap() wird während des Bootstrap-Prozesses aufgerufen. Hier werden in der Regel Listener im EventManager registriert (dazu später mehr). Wichtig: Für jedes installierte und aktivierte Modul wird diese Methode bei jedem Seitenaufruf ausgeführt. Es sollten hier nur die Dinge geschehen, die für das Setup des Moduls zwingend notwendig sind. Die Methode getConfig() stellt sicher, dass unsere Konfigurationsdatei /config/module.config.php geladen wird. Den Sinn und Zweck der Methode getAutoloaderConfig() erläutert wir im nächsten Abschnitt.

Aufmacherbild: viewfinder of sniper rifle made in 2d software von Shutterstock / Urheberrecht: jovan vitanovski

[ header = Autoloading für Module ]

Autoloading für Module

Ein großes Problem beim ZF1 betraf das Autoloading, das nicht für seine Performanz berühmt war. Für das ZF2 wurde ein neues Konzept für das Autoloading von Modulen entwickelt. So gibt es verschiedene Möglichkeiten, um die Dateien zu laden. Ein Überblick verschafft das Referenzhandbuch zur ZendLoader-Komponente.

In der getAutoloaderConfig()-Methode aus Listing 2 sehen wir den Standardweg. Der StandardAutoloader benötigt die Angabe des Namensraumes, in dem nach Klassen gesucht werden soll. Werden die Konstanten in __NAMESPACE__ => __DIR__ . ‚/src/‘ . __NAMESPACE__ aufgelöst, würde dort ‚Application‘ => ‚/home/devhost/zf2phpmagazin/module/Application/src/Application‘ stehen. Möchten wir also die Klasse ApplicationControllerIndexController verwenden, so wird das auf die folgende Datei abgebildet und diese dann geladen:

/home/devhost/zf2phpmagazin/module/Application/src/Application/IndexController.php

Neben dem StandardAutoloader gibt es weitere Autoloader, wobei der ClassMapAutoloader die beste Performanz verspricht. Die Idee einer ClassMap besteht in einem Array, das die zu ladenden Klassen auf die entsprechenden Dateien mappt. Das Autoloading kann so konfiguriert werden, dass mithilfe einer ClassMap nach einer Klasse gesucht wird. Schlägt dieser Versuch fehl, kann auf den StandardAutoloader zurückgegriffen werden.

In Listing 3 ist diese Erweiterung zu sehen. Hier kommt eine Datei autoload_classmap.php zum Einsatz, die bisher noch nicht existiert und die wir anlegen müssen. Glücklicherweise bietet das ZF2 ein entsprechendes Skript, das sich um die Erstellung der ClassMap kümmert:

> cd /home/devhost/zf2phpmagazin/module/Application/ 
> php ../../vendor/zendframework/zendframework/bin/classmap_generator.php

Nun sollte die Datei /module/Application/autoload_classmap.php existieren und so wie in Listing 5 aussehen. Sie enthält Einträge für unser Modul Klasse und unseren IndexController. Wer Langeweile verspürt, kann spaßeshalber die Datei /module/Application/src/Application/Foo/Bar.php mit einer leeren Klasse Bar anlegen und danach das classmap_generator.php-Skript erneut starten. Die autoload_classmap.php erneut öffnen und schon sollte die neue Klasse enthalten sein.

Alles ist ein Modul

Ein wichtiges neues Konzept im Zend Framework 2 ist die Modularität. Waren die Module im ZF1 weder Fisch noch Fleisch und nur schwer wiederverwendbar und austauschbar, so ist im ZF2 alles ein Modul. Wie wir schon gesehen haben, liegt auch unsere Anwendung im Application-Modul. Wenn wir im ZF2 ein Blogmodul erstellen, ist dafür gesorgt, dass wir dieses Modul in anderen Anwendungen wiederverwenden können. Basis ist der neue ModuleManager, der sich um das Laden und Ausführen der Module kümmert.

Wir legen jetzt ein neues Modul an, das einen Controller und eine Action enthält, aber dennoch alle wesentliche Aspekte eines Moduls berührt. Die Schritte in der Konsole für das Anlegen des Moduls, der Verzeichnisstruktur und der leeren Dateien sind auf GitHub in Listing 6 zu finden.

Wir kopieren den Inhalt aus Listing 7 in die Datei /module/Honk/Module.php. Oben definieren wir den Namensraum für unser Modul und verwenden denselben Namen wie für das Modulverzeichnis. Die Klasse Module beinhaltet zwei bereits bekannte Methoden. Mit getConfig() wird die Konfigurationsdatei /module/Honk/config/module.config.php geladen und mit getAutoloaderConfig() ein Stack für den Autoloader aufgebaut. Die Datei /module/Honk/autoload_classmap.php müssen wir noch erstellen.

Als Nächstes kopieren wir den Inhalt aus Listing 8 in die Datei /module/Honk/config/module.config.php. An dieser Stelle möchte ich nicht auf die Details des Routings unter dem Konfigurationsparameter router einzugehen. Wahrscheinlich fragt sich so mancher, warum wir den Controller explizit im Konfigurationsparameter controllers konfigurieren müssen. Controller werden im ZF2 nicht mehr automatisch gelesen und erkannt. Das Scannen des Controller-Verzeichnisses ist zu langsam, sodass wir ähnlich wie beim Autoloading auch für die Controller eine Art ClassMap erstellen. Im Konfigurationsparameter view_manager wird ähnlich wie bei den Controllern definiert, wo die View-Skripte zu finden sind.

Der Inhalt aus Listing 9 auf GitHub wird nach /module/Honk/src/Honk/Controller/IndexController.php übertragen. Zu Beginn wird ein Unternamensraum für die Controller unseres Moduls Honk definiert. Der IndexController besteht aus einer Aktionsmethode indexAction(). Im ZF1 mussten Template-Variablen immer explizit an das View-Objekt übergeben werden. Im ZF2 können alle Template-Variablen als Array zurückgegeben werden. Oder es wird ein ViewModel erstellt und dieser Instanz alle Variablen übergeben. Neben dem ViewModel gibt es übrigens auch noch JsonModel, FeedModel und ConsoleModel.

Bleibt noch unser ViewSkript aus Listing 10, das in die Datei /module/Honk/view/honk/index/index.phtml kopiert wird. Zu dem HTML-Code nur zwei Anmerkungen: zum einen nutzt die Skeleton Application für die Darstellung Twitter Bootstrap. Die CSS-Klasse hero-unit macht sich hiermit Twitter Bootstrap zunutze. Zum anderen kann auf die Template-Variablen auch direkt zugegriffen werden. Der Aufruf von $foo funktioniert genauso wie $this->foo.

Bevor wir das Modul in Aktion erleben können, müssen wir es aktivieren und die ClassMap-Datei erstellen lassen. Für die Aktivierung des Moduls öffnen wir die Datei /config/application.config.php und fügen ‚modules‘ den Eintrag ‚Honk‘ hinzu. Die ClassMap generieren wir wie folgt:

> cd /home/devhost/zf2phpmagazin/module/Honk/ 
> php ../../vendor/zendframework/zendframework/bin/classmap_generator.php

Wenn wir nun http://zf2phpmagazin/honk aufrufen, sollten wir ein Ergebnis wie in Abbildung 2 erhalten – unser erstes ZF2-Modul läuft.

Abb. 2: Unser erstes ZF2-Modul läuft

[ header = Dependency Injection und ServiceLocator ]

Dependency Injection und ServiceLocator

Das Thema Dependency Injection geistert schon seit einigen Jahren durch die PHP-Welt. Auch das ZF2 macht sich dieses Konzept zunutze. Ein Beispiel zur Erklärung:

$ml = new MovieLister(new MovieFinder());

Der MovieLister ist abhängig (dependency) von dem MovieFinder, wobei der MovieFinder in den MovieLister injiziert (injection) wird. Bei der Dependency Injection geht es um die Steuerung der Abhängigkeiten von Objekten. Das ZF2 Manual bietet eine kleine Einführung in das Thema [17] und mit ZendDi die passende Komponente. Doch jetzt vergessen wir diese Komponente gleich wieder.

Je weiter die Entwicklung voran schritt, desto häufiger traten Fragen zu der ZendDi-Komponente auf. Hauptkritikpunkt war, dass sie für Einsteiger zu kompliziert sei. Deshalb wurde die Idee des ServiceManagers geboren.

Der ServiceManager implementiert das ServiceLocator-Entwurfsmuster. Er stellt nicht wie Zend_Registry aus dem ZF1 nur fertige Objekte bereit und macht sie anderen Objekten zugänglich, sondern kümmert sich auch um die Instanziierung dieser Objekte. Der ServiceLocator kann über die applikationsweite sowie die modulspezifischen Konfigurationsdateien und die Module.php-Klassen konfiguriert werden. Ein einfaches Beispiel findet sich in der Datei /module/Application/config/module.config.php:

 

'service_manager' => array(
  'factories' => array(
    'translator' => 'ZendI18nTranslatorTranslatorServiceFactory',
  ),
),

Der ServiceLocator wird über den Konfigurationsparameter service_manager konfiguriert. In diesem Fall erfolgt die Angabe einer Factory-Klasse, die sich um die Erstellung des Objektes kümmert. In Listing 11 ist die Klasse TranslatorServiceFactory dargestellt. Sie implementiert ZendServiceManagerFactoryInterface und beinhaltet die Methode createService(). Über den ServiceLocator wird auf das Konfigurationsobjekt zugegriffen. Darin wird nach den Konfigurationsdaten für translator gesucht. Diese Daten werden an die Factory des Translators übergeben und das erstellte Objekt wird zurückgegeben.

Wenn über den ServiceLocator auf das translator-Objekt zugegriffen werden soll, wird zuerst geprüft, ob das Objekt existiert. Ist dies nicht der Fall, wird die TranslatorServiceFactory aufgerufen und der Service erstellt. Danach wird diese Instanz im ServiceLocator gespeichert und an den Aufruf zurückgegeben. Bei jeder neuen Anforderung im selben Prozess wird diese Instanz direkt zurückgegeben.

Neben Factory-Klassen kann der ServiceLocator auch Closures verarbeiten. Gäbe es keine Klasse TranslatorServiceFactory, könnte der Closure so aussehen wie in Listing 12. Neben factories können auch invokables angegeben werden. Dies ist für alle Klassen nützlich, die keine weiteren Abhängigkeiten zu anderen Klassen haben. Das Prinzip kennen wir bereits bei der Definition der Controller:

'controllers' => array(
  'invokables' => array(
    'ApplicationControllerIndex' => 'ApplicationControllerIndexController'
   ),
),

Moment mal! Wer aufgepasst hat, erkennt die Angabe des Konfigurationsparameters controllers. Doch ging es nicht gerade um service_manager? Richtig, denn wir müssen bei den ServiceManagern vier Implementierungen unterscheiden:

  1. ZendServiceManagerServiceManager kümmert sich um alle Services für die Applikation und wird mit dem Parameter service_manager konfiguriert.
  2. ZendMvcControllerControllerManager kümmert sich um alle Controller und wird mit dem Parameter controllers konfiguriert.
  3. ZendMvcControllerPluginManager kümmert sich um alle Controller Plug-ins und wird mit dem Parameter controller_plugins konfiguriert.
  4. ZendViewHelperPluginManager kümmert sich um alle View Helper und wird mit dem Parameter view_helpers konfiguriert.

Würden alle Services, Controller, Plug-ins und View Helper in einem ServiceLocator verwaltet, gäbe es Probleme, wenn es ein Plug-in und einen View Helper mit demselben Namen gäbe. Dieses Problem gibt es bereits mit dem URL View Helper und dem URL Controller Plug-in.

Zurück zum Thema: Neben Factory-Klassen und Factory-Closures sowie den Invokables können die ServiceManager auch mit Abstrakten Factories und Aliases gefüttert werden.

Wer die Konfiguration nicht über die module.config.php vornehmen möchte, hat eine weitere Möglichkeit. Die Klasse Module kann mit vier Methoden versehen werden, die jeweils ein Array mit den Konfigurationsdaten zurückgeben müssen:

  1. getServiceConfig() konfiguriert die Services für den ServiceManager.
  2. getControllerConfig() konfiguriert die Controller für den ControllerManager.
  3. getControllerPluginConfig() konfiguriert die Plug-ins im Controller PluginManager.
  4. getViewHelperConfig() konfiguriert die View Helper im View Helper PluginManager

ServiceManager richtig einsetzen

Vielen Einsteigern in das ZF2 stellt sich die Frage, wie man auf die Service Manager zugreifen kann, um sich Objekte zu holen. Aus einem Controller ist dies über folgenden Aufruf möglich: $translator = $this->getServiceLocator()->get(‚translator‘);

Auch die View Helper und die Controller-Plug-ins verfügen über eine Zugriffsmöglichkeit auf den View Helper PluginManager sowie den Controller PluginManager und zusätzlich auf den ServiceManager. An dieser Stelle sei jedoch dringend von dieser Vorgehensweise abgeraten! Wenn wir von überall her auf den ServiceManager zugreifen, bringen wir uns um den Lohn der Vorteile der Dependency Injection. Zudem müssen wir den gesamten Code durchwühlen, um alle Abhängigkeiten einer Klasse z. B. eines Controllers zu finden.

An einem kleinen Beispiel lernen wir, wie man es richtig macht. Als Erstes erstellen wir einen kleinen Service, den wir in einem View Helper verwenden möchten. Der Service gibt einen zufälligen Namen aus. Wir erstellen die Datei /module/Honk/src/Honk/Service/RandomName.php inklusive nicht vorhandener Verzeichnisebenen und fügen den Code aus Listing 13 ein.

Als Nächstes erstellen wir einen View Helper, der diesen Service verwenden soll, um einen zufälligen Namen auszugeben. Dafür legen wir die Datei /module/Honk/src/Honk/View/Helper/RandomName.php inklusive nicht vorhandener Verzeichnisebenen an und fügen den Inhalt von Listing 14 ein. Ausnahmsweise verfügt diese Datei auch über Docblocks, um den Einsatz der Klasse über eine Autovervollständigung des IDE nutzen zu können. Der View Helper nimmt im Konstruktor unseren RandomName-Service entgegen und verfügt über entsprechende Setter- und Getter-Methoden. Anders als im ZF1, wo ein View Helper zwingend den Klassennamen mit führendem Kleinbuchstaben als Methode für die Ausführung benötigte, muss ein View Helper im ZF2 die __invoke()-Methode implementieren.

Die Vorarbeiten sind abgeschlossen, widmen wir uns der Konfiguration. Wie wir gelernt haben, werden View Helper und Services über verschiedene ServiceManager verwaltet. Statt über die modul.config.php wollen wir die Konfiguration auch über unsere Module.php umsetzen. Wir fügen der Datei /module/Honk/Module.php somit die Methoden aus Listing 15 hinzu. Die Methode getServiceConfig() stellt unseren RandomName Service als invokable bereit, da der Service keinen Konstruktor hat. Die Methode getViewHelperConfig() stellt eine Factory für den RandomName View Helper bereit, der den RandomName-Service an seinen Konstruktor übergeben bekommt. Die Übergabe des Services an den View Helper im Konstruktor ist am Ende das, was allgemein als Dependency Injection bezeichnet wird.

Nach der Konfiguration können wir den neuen View Helper ausprobieren. Dafür öffnen wir die Datei /module/Honk/view/honk/index/index.phtml und ändern den Inhalt ab, wie es das Listing 16 zeigt. Rufen wir die Seite http://zf2phpmagazin/honk im Browser auf, sollte uns bei jedem Aufruf ein anderer Name ausgegeben werden.

[ header = Ein paar Hinweise zum Schluss]

Ein paar Hinweise zum Schluss

Obwohl die Controller über eine Methode $this->getServiceLocator() verfügen, sollten auch alle Abhängigkeiten in einer Factory für den Controller übergeben werden.

Ob die Konfigurationen für die vier ServiceManager in der Konfigurationsdatei oder in der Modul-Klasse abgelegt werden, ist Geschmacksache. Wir könnten die Servicekonfigurationen auch in einer separaten Datei vorhalten.

Ob man sich für Closures oder Factory-Klassen entscheidet, bleibt einem selbst überlassen. Factory-Klassen sind in der Regel leichter testbar, Closures ggf. schneller verarbeitet. Manche Entwickler halten sich an folgende Faustregel: Wenn du mehr als fünf Factories in einem Modul benötigst, dann verwendende Factory-Klassen. Für kleinere Module reichen Closures. Andere Entwickler ignorieren diese Faustregel aber gerne.

Standard-Events nutzen

Neben dem neuen ModuleManager und dem ServiceManager gibt es ein weiteres Konzept, das Einsteiger ins Zend Framework 2 verinnerlichen sollten. Bei der Event-driven Architecture (ereignisgesteuerte Architektur) wird das Zusammenspiel der Komponenten durch Events (Ereignisse) gesteuert. Ein Listener (Beobachter) lauscht, ob ein Ereignis getriggert (ausgelöst) wurde, und führt dann die notwendigen Aktionen für das Ereignis aus.

Das ZF2 bietet mit dem EventManager eine Komponente, die im Framework an vielen Stellen genutzt wird. Besonders der ModuleManager sowie die MVC- und View-Komponenten nutzen den EventManager für die Steuerung der Abläufe. Für unser Anwendung im MVC sind folgende Events standardmäßig definiert (eine vollständige Liste der Events findet sich bei Rob Allen ) und können von uns genutzt werden:

  1. bootstrap
  2. route
  3. dispatch
  4. render
  5. finish

An dieser Stelle möchten wir ein Problem mit dem EventManager lösen, vor dem jeder ZF2-Entwickler früher oder später steht: unser Layout besteht derzeit nur aus einem Template. Doch in vielen Anwendungen wird ein eigenes View-Skript für Header, Footer, Seitenleiste usw. verwendet. Dieses Problem möchten wir mit dem EventManager lösen.

Zuerst schreiben wir eine Listener-Klasse, welche die Registrierung des Events und die Methode zur Umsetzung unserer Layoutsegmente beinhaltet. Dafür erstellen wir die Datei /module/Application/src/Application/Listener/ApplicationListener.php inklusive der nicht vorhandenen Verzeichnisebenen und fügen den Inhalt von Listing 17 ein.

Unser ApplicationListener implementiert das ListenerAggregateInterface, das die Implementierung der Methoden attach() und detach() erfordert. In der attach()-Methode wird im EventManager für das render-Event unser ApplicationListener mit der Methode renderLayoutSegments() registriert. Durch die „-100“ geben wir an, dass die Methode renderLayoutSegments() nach dem eigentlichen Rendern aufgerufen werden soll. Die detach()-Methode stellt sicher, dass der ApplicationListener wieder vom EventManager entkoppelt wird.

In der Methode renderLayoutSegments() werden die Layoutsegmente für den Header und den Footer verarbeitet. Im Wesentlichen läuft dies so ab, dass auf das ViewModel des MvcEvents zugegriffen wird und ihm zwei weitere ViewModels als Kinder hinzugefügt werden. Dabei geben wir das jeweilige Template für den Header und den Footer mit an.

Damit unser ApplicationListener vom EventManager auch verarbeitet werden kann, müssen wir ihn diesem bekannt machen. Wir öffnen die Datei /module/Application/Module.php und ändern die Klasse so ab, wie in Listing 18 angegeben. Bitte nicht die Angabe des Namensraums für unseren ApplicationListener vergessen. Mit dem neuen Einzeiler wird unser ApplicationListener initialisiert und dem EventManager durch Hinzufügen bekannt gemacht. Dabei wird die attach()-Methode unseres ApplicationListener aufgerufen.

Als Nächstes wollen wir die neuen Layoutsegmente im ViewManager bekannt machen. Wir öffnen die Datei /module/Application/config/module.config.php und ergänzen im Konfigurationsparameter view_manager die Einträge in der template_map, wie in Listing 19 angegeben.

Nun geht es an die Aufsplittung des Layoutskripts. Wir erstellen die Datei /module/Application/view/layout/footer.phtml und fügen den Inhalt von Listing 20 ein. Danach erstellen wir die Datei /module/Application/view/layout/header.phtml und fügen den Inhalt von Listing 21 ein. Zudem öffnen wir die Datei /module/Application/view/layout/layout.phtml und überschreiben den Inhalt mit dem von Listing 22. Wir sehen, dass wir zusätzlich zu content die Layoutsegmente header und footer verwenden.

Rufen wir http://zf2phpmagazin/honk im Browser auf, sollte die Seite so aussehen wie vorher. Wer nicht glaubt, dass es geklappt hat, kann im Footer-View-Skript einen zusätzlichen Text eingeben. Nach einem Reload im Browser sollte dieser Text ausgegeben werden.

Der EventManager lässt sich nicht nur für Standard-Events, sondern auch für eigene Events verwenden. Wir können uns z. B. ein Objekt Order vorstellen. Wurde eine neue Bestellung angelegt, könnte das Objekt ein Event triggern und ein Listener könnte z. B. eine Bestellbestätigung per E-Mail an den Kunden versenden oder die Warenwirtschaft aktualisieren. Der eigenen Fantasie und den Anforderungen sind keine Grenzen gesetzt.

Module installieren

Auf der bereits erwähnten Website sind bereits eine Vielzahl von fertigen Modulen zu finden, die wir in unsere Anwendung integrieren können. Empfehlenswert für den Einstieg sind unter anderem diese Module:

  1. BjyProfiler
  2. DoctrineModule
  3. ZfcUser
  4. ZendDeveloperTools

Exemplarisch gehen wir die Installation anhand des Moduls „ZendDeveloperTools“ durch. Die Installation kann direkt über Composer erfolgen. Dafür öffnen wir die Datei composer.json im Hauptverzeichnis unseres Projektes. Den require-Bereich ändern wir ab, wie in Listing 23 angegeben. Danach installieren wir das neue Modul mit Composer:

> php composer.phar self-update

Für die Aktivierung des Moduls öffnen wir die Datei /config/application.config.php und fügen ‚modules‘ den Eintrag ‚ZendDeveloperTools‘ hinzu. Zuletzt müssen wir eine Konfigurationsdatei kopieren:

> cd /home/devhost/zf2phpmagazin/
> cp vendor/zendframework/zend-developer-tools/config/zenddevelopertools.local.php.dist config/autoload/zenddevelopertools.local.php

Nach der Installation rufen wir http://zf2phpmagazin/honk im Browser auf und staunen über die schicke Toolbar am Ende der Seite. Leider lassen sich nicht alle Module so reibungslos per Composer installieren. Basis hierfür ist der Eintrag des Moduls auf http://packagist.org/. Mit diesem Eintrag kann Composer allein durch Hinzufügen des Requirements die Installation ausführen, da er sich alle Daten von der genannten Website holen kann. Module können auch per Git geklont oder simpel als ZIP heruntergeladen und entpackt werden. Bei den besseren Modulen gibt es in der Regel auch immer eine Installationsanleitung. Jeder interessierte ZF2-Entwickler sollte die Modules-Website im Auge behalten. In den kommenden Monaten werden viele neue Module eingetragen werden. Beim Einsatz des einen oder anderen Moduls wird man jede Menge Zeit sparen.

Fazit

Der Einstieg in das Zend Framework 2 erscheint vielen Entwickler zu Beginn als sehr komplex. Je tiefer man einsteigt und je mehr das Verständnis wächst, desto schneller beginnt man, die Vorzüge des Zend Framework 2 zu schätzen. Wer sich mit dem neuen Ecosystem, dem Autoloading, der neuen Modularität, dem ServiceManager und dem EventManager des ZF2 vertraut macht, hat schon die halbe Miete in der Tasche. Ich hoffe, dieser Artikel konnte ein wenig bei der Erkenntnisgewinnung hilfreich sein.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -