Laravel 4 – Static or not static. That is the question.

Als ich das erste Mal das noch zu unbekannte Framework Laravel vor der Nase hatte, sind mir sehr schnell die statischen Zugriffe der API aufgefallen. Wo sich viele qualitätsbewusste Entwickler heutzutage die „und wie soll man das testen“ Frage stellen, stand ich hier vor verschlossenen Türen.

THE PHP FRAMEWORK FOR WEB ARTISANS.

Hier scheint sich Laravel 4 eines uns sehr gut bekannten Design Patterns zu bedienen. Die Rede ist von einer Facade. Jede Klasse welche scheinbar statische Methoden bereitstellt, erbt von einer „Facade“-Klasse. Wenn wir also „Config::get(‚database‘)“ aufrufen, wird eine fast leere Facade benutzt. Diese kennt lediglich ihren Accessor (facadeAccessor = ‚config‘) und erweitert entsprechend die Laravel Facade. Die eigentliche Implementierung der genutzten Methode ist dort also gar nicht enthalten.

Das führt uns zu zwei Fragen:

  • Welche Klasse wird bei dem Genannten nun wirklich aufgerufen?
  • Und woher kennt die Facade augenscheinlich diese Klasse?

Nun dann schauen wir mal genauer hin.

Die Facade kann uns mit „Config::getFacadeRoot()“ die eigentlich aufgerufene Klasse als String ausgeben. In diesem Fall ist das die „Illuminate\Config\Repository„-Klasse welches scheinbar im Hintergrund initialisiert wurde. Sie hat keine statischen Methoden, aber sie besitzt dynamisch die von uns genutzte „get„-Methode.

Okay, wir haben die Funktionalität scheinbar gefunden welche tatsächlich hinter der Facade steckt.

  • Wie also weiss die Facade nun, welche Klasse sie initialisieren muss?
  • Und vor allem welcher Technik bedient sich Laravel 4 hier?

Intern versucht die Facade in einem Register „resolvedInstance“ den aktuell gesuchten FacadeAccessor (Config) aufzulösen. Kann die Instanz nicht aufgelöst werden, fragt diese bei unserer Hauptkomponente „Application“ nach, wie er das auflösen soll. Ist das auch nicht möglich, so liegt ein Fehler vor welchen wir fixen müssen. ;-)

FAZIT

Am Ende haben wird das ganze also durch eine Dependency Injection aufgelöst (früher in etwas generischer Form auch Inversion of Control genannt).

Nun haben wir die Rätsel ja gelöst, oder?
Warum das ganze überhaupt?

Laravel 4 bietet durch die Facaden einen unglaublich einfachen Zugriff auf die meist benutzen Komponenten vom Framework. Zusätzlich sind diese durch Dependency Injection austauschbar und somit testbar.

Nun kann ich ja viel behaupten, deshalb hier ein anschauliches Beispiel:

Der erste Test stellt sicher das wir unsere tatsächliche Config-Klasse benutzen und schaut was wir aktuell für einen Datenbank-Adapter nutzen. Dieser scheint „mysql“ zu sein.

Soweit so gut.

Nun haben wir einen zweiten Test geschrieben welcher unsere eigene Config-Implementierung DummyConfig nutzen soll. Diese gibt erstmal nur „notMysql“ aus.

Was haben wir also geschafft? Wir konnten die aktuell instanzierte Config-Klasse mit unserer eigenen Implementierung überschreiben. Genial!

Um das noch kurz klar zu stellen. Laravel bietet die Möglichkeit ein Objekt zu instanzieren NUR wenn es noch keins gibt, eines zu überschreiben, oder zu einer Facade Listener/Composer anzufügen. Außdem ist die Umsetzung des Tests oben in der Realität ein Anti-Pattern, aber veranschaulicht hier gut was ich sage möchte.

Ich hoffe das Gerücht „Laravel 4 hat zu viele statische Klassen! Das ist schlecht!“ konnte hiermit aus der Welt geschafft werden und ich wünsche fröhliche Adventstage.

Noch 4 Tage bis zum zweiten Advent. :-)

Quellen und Lesehinweise

Für neue Blogupdates anmelden:


6 Gedanken zu “Laravel 4 – Static or not static. That is the question.

  1. Hallo Steven,

    verstehe ich es richtig das du damit sagen willst, dass man in Tests den „Container“ mit den Objekten überschreiben und mit Mock-Objekten ersetzen kann?

    Gibt es in Laravel die Möglichkeit die Abhängigkeiten explizit zu definieren und über Constructor-/Setter-Injection zu übergeben? Das erleichtert meiner Meinung nach die Analyse und Verständlichkeit sehr, da ich alles auf einen Blick sehen kann.

    Viele Grüße

    • Hallo Marco,

      absolut richtig. Wir können hier mocken oder stubben je nachdem wie du das ganze testen möchtest.

      Laravel bietet hier unterschiedliche Ansätze:
      – über den Constructor (wie die Controller bei Symfony2 durch Code Reflection)
      – durch explizites binden über einen eigenen ServiceProvider (per Hand das Objekt binden)
      – durch impliziertes binden (z. B. ein ModelInterface im Constructor type-hinten, aber ein MysqlModel benutzen)
      und noch ein paar mehr, aber das würde zu weit gehen.

      Ich nehme mal an du spielst auf die „Dependency Konfiguration“ von Symfony 2 an.
      Das könnte man recht einfach selbst implementieren, gibt es in der Form nach meinem Wissen nicht. ;-)

      Viele Grüße,
      Steven.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.