Weg von der Magie - oder: warum ich Laravel nicht mehr benutzen will

Es ist Zeit für eine Änderung der Werkzeuge, die ich benutze. Und ich sage dir warum!

Zunächst möchte ich sicherstellen, dass Sie über meine Absichten Bescheid wissen. Ich versuche nicht, über Laravel zu schimpfen oder warum andere Frameworks besser sein könnten.

Dieser Artikel ist sehr subjektiv. Ich werde Ihnen meine Gedanken geben und versuchen, Sie dazu zu bringen, auch Ihre Rahmenentscheidungen zu überdenken. Und wenn Sie nach der Neubewertung bei Laravel bleiben, ist das in Ordnung. Ich habe nicht die Absicht, Menschen von Laravel zu anderen Frameworks oder Sprachen zu konvertieren. Es ist jedoch wichtig, genauer hinzuschauen und sicherzustellen, dass Sie wissen, was Sie verwenden und warum Sie es verwenden.

Intro

Ich arbeite jetzt seit ungefähr 2 Jahren mit Laravel. Ich habe es immer genossen, wie einfach es war, eine Anwendung zu starten und in wenigen Minuten loszulegen. Es bietet so viele nützliche Tools aus der Box. Die Konsolenbefehle unterstützen Sie in jeder Hinsicht beim Codieren. Sie generieren Klassen, Gerüste für die Authentifizierung und vieles mehr.

Aber je tiefer Sie gehen und je größer die Projekte werden, desto komplizierter wird die Entwicklung mit Laravel. Oder lassen Sie es mich umformulieren: Je besser andere Tools die Arbeit erledigen. Ich sage nicht einmal, dass es nur Laravels Schuld ist. Dies liegt auch teilweise daran, dass PHP nicht sehr gut gestaltet ist.

Jetzt fangen wir an!

Beredtes ORM

Wenn Sie bereits mit Laravel gearbeitet haben, kennen Sie Eloquent sicherlich. Es ist das ORM, das mit einer Standardinstallation geliefert wird. Es kommt mit vielen netten Funktionen. Das Design macht Ihre Anwendung jedoch unnötig komplex und verhindert, dass die IDE Ihren Code korrekt analysiert.

Dies ist teilweise auf das verwendete ORM- Muster für aktive Datensätze zurückzuführen, und teilweise auf die Tatsache, dass Eloquent den Entwickler davon abhalten möchte, mehr Code schreiben zu müssen. Zu diesem Zweck kann der Entwickler viel in das Modell einfügen, das nicht dorthin gehört.

Klingt nach guten Absichten, aber ich fing an, dies immer mehr abzulehnen.

Schauen wir uns ein Beispiel an:

Das erste, was Sie sehen, ist, dass das Modell keine Eigenschaften enthält . Das scheint irrelevant, aber für mich macht es einen großen Unterschied. Alles wird „magisch“ in die Klasse eingefügt, indem die Tabellenmetadaten gelesen werden. Natürlich versteht Ihre IDE das nicht ohne Hilfe. Und Sie haben keine Möglichkeit, Ihre Eigenschaften anders als Ihre Spalten zu benennen.

Schauen Sie sich nun die Scope-Methode an. Für Laravel-Benutzer ist ziemlich klar, was es tut. Wenn Sie diese Methode aufrufen, wird die zugrunde liegende SQL-Abfrage durch Hinzufügen der angegebenen WHERE-Klausel erfasst.

Sie können sehen, es ist nicht statisch. Das würde bedeuten, dass diese Methode auf ein bestimmtes Objekt der Klasse angewendet wird. In diesem Fall ist dies jedoch nicht der Fall . Ein Bereich wird in einem Abfrage-Generator aufgerufen. Es hat nichts mit dem Modellobjekt selbst zu tun. Ich werde das erklären, nachdem Sie gesehen haben, wie Sie diese Bereiche normalerweise nennen:

Sie rufen eine statische Methode auf popular(), die noch niemand definiert hat. Aber da Laravel eine __call()und __callStatic()Methode definiert , wird sie durch sie gehandhabt. Diese Methoden leiten den Aufruf an einen Abfrage-Generator weiter.

Dies ist nicht nur etwas, das Ihre IDE nicht versteht. Dies erschwert das Refactoring, kann neue Entwickler verwirren und die statische Analyse wird ebenfalls schwieriger.

Wenn Sie solche Methoden in Ihr Modell einfügen, verletzen Sie außerdem das S von SOLID. Falls Sie damit nicht vertraut sind, ist SOLID ein Akronym für:

  • S ingle Prinzip Verantwortung
  • O Stift / Geschlossenes Prinzip
  • L iskov-Substitutionsprinzip
  • I nterface Segregationsprinzip
  • D ependenzinversionsprinzip

Wenn Sie Eloquent verwenden, haben Ihre Modelle mehrere Verantwortlichkeiten. Es enthält die Daten aus Ihrer Datenbank, wie dies normalerweise bei Modellen der Fall ist, enthält jedoch auch Filterlogik, möglicherweise Sortierung und noch mehr. Das willst du nicht.

Globale Helfer

Laravel verfügt über einige globale Hilfsfunktionen. Sie scheinen sehr praktisch zu sein und ja, sie sind praktisch.

Sie müssen nur wissen, dass Sie Ihre Unabhängigkeit für diese Handlichkeit opfern und Ihr globaler Namespace verschmutzt wird. Es führt selten zu Konflikten, aber es sollte bevorzugt werden, dies insgesamt zu vermeiden.

Schauen wir uns einige Beispiele an. Hier ist eine Liste von drei Hilfsmethoden, die wir haben, aber nicht brauchen, da es bessere Alternativen gibt:

  • app_path()- Warum? Wenn Sie den Pfad der App benötigen, fragen Sie das App-Objekt. Sie erhalten es durch Tipphinweise.
  • app()- huh? Wir brauchen diese Methode nicht. Wir können die App-Instanz injizieren!
  • collect()- Dadurch wird eine neue Instanz der Collection-Klasse erstellt. Wir können einfach selbst ein Objekt neu erstellen.

Ein weiteres konkretes Beispiel:

Wir verwenden den globalen request()Helfer von Laravel , um die POST-Daten abzurufen und als Attribute in unser Modell aufzunehmen.

Anstatt den globalen Helfer zu verwenden, können Sie Requestin der Controller-Methode einen Hinweis als Objekt als Parameter eingeben. Der Dispatcher in Laravel weiß, wie er uns das benötigte Objekt zur Verfügung stellen kann. Es wird unsere Methode damit aufrufen und wir müssen keinen Helfer aufrufen.

Und wir können noch einen Schritt weiter gehen, um noch mehr zu entkoppeln. Laravel ist PSR-7-konform. Anstatt den Hinweis auf das Anforderungsobjekt einzugeben, können Sie auch einen Hinweis eingeben ServerRequestInterface. Auf diese Weise können Sie das gesamte Framework durch alles ersetzen, was PSR-7-kompatibel ist. Alles in dieser Methode wird weiterhin funktionieren. Dies würde fehlschlagen, wenn Sie weiterhin die Hilfsmethoden verwenden. Das neue Framework würde nicht mit der Hilfsmethode geliefert, und daher müssten Sie diesen Teil Ihres Codes neu schreiben.

Sie wechseln selten das gesamte Framework, aber es gibt Leute, die dies tun. Und selbst wenn Sie möglicherweise nie wechseln, ist dies für die Interoperabilität wichtig. In der Lage zu sein, Abhängigkeiten einzufügen und einen präzisen Datenfluss zu haben, anstatt Abhängigkeiten und Daten von innen nach außen aufzulösen und anzufordern, ist der richtige Weg. Es erleichtert das Testen, Refactoring und fast alles, wenn Sie es in den Griff bekommen.

Ich war glücklich, als ich las, dass mit Laravel 5.8 die String- und Array-Helfer aus dem Kern entfernt und in ein separates Paket gestellt werden. Dies ist ein guter erster Schritt. Die Dokumentation sollte jedoch die Verwendung aller Hilfsfunktionen behindern.

Fassaden

Auch hier kommen die Argumente aus dem letzten Teil ins Spiel. Fassaden scheinen ein gutes Werkzeug zu sein, um schnell auf einige Methoden zuzugreifen, die nicht wirklich statisch sind. Aber sie binden dich wieder in den Rahmen ein. Sie verwenden sie, um Abhängigkeiten manuell aufzulösen, anstatt die Umgebung anzuweisen, sie bereitzustellen.

Gleiches gilt für die Komplexität, indem alles durch die magischen Methoden geleitet wird.

Da wir über IDE-Unterstützung gesprochen haben, weiß ich, dass einige von Ihnen mich möglicherweise auf das IDE-Hilfspaket von barryvdh verweisen. Das musst du nicht. Ich kenne dieses Paket bereits. Aber warum wird es überhaupt gebraucht? Weil einige Designentscheidungen in Laravel einfach nicht gut sind. Es gibt Frameworks, in denen Sie das nicht brauchen. Nehmen Sie zum Beispiel Symfony. IDE-Hilfsdateien sind nicht erforderlich, da sie gut entworfen und implementiert sind.

Anstelle von Fassaden könnten wir wieder die Abhängigkeitsinjektion verwenden, wie wir es im vorherigen Beispiel getan haben. Wir hätten ein echtes Objekt und könnten echte Methoden darauf aufrufen. Viel besser.

Ich werde Ihnen noch einmal ein Beispiel geben:

Wir könnten das leicht aufräumen. Sagen wir Laravel, er soll eine injizieren ResponseFactoryund uns die aktuelle Anfrage weiterleiten:

Wir haben jetzt erfolgreich die Verwendung von Fassaden aus unserer Steuerung entfernt. Der Code sieht immer noch sauber und kompakt aus, wenn nicht sogar besser als zuvor. Und da unsere Controller immer die allgemeine ControllerKlasse erweitern, können wir alles noch einen Schritt weiter gehen, indem wir die Antwortfactory auf diese übergeordnete Klasse verschieben. Wir brauchen es sowieso in allen anderen Controller-Klassen.

Ich habe gehört, dass einige Leute "zu viele Konstruktorparameter" als Argument gegen das Injizieren von allem angeben. Dem stimme ich aber nicht zu. Es verbirgt in erster Linie nur die Abhängigkeiten und damit die Komplexität. Wenn Sie 10 bis 20 Argumente nicht in Ihrem Konstruktor haben möchten, haben Sie Recht.

Die Lösung ist jedoch keine Magie. Wenn so viele Abhängigkeiten in einer einzelnen Klasse benötigt werden, hat diese Klasse höchstwahrscheinlich zu viele Verantwortlichkeiten. Anstatt diese Komplexität zu verbergen, überarbeiten Sie diese Klasse. Teilen Sie es in neue auf und verbessern Sie Ihre Anwendungsarchitektur.

Unterhaltsame Tatsache: Es gibt ein echtes Designmuster namens „Fassadenmuster“, das im Buch der Gang of Four vorgestellt wird. Aber es hat eine ganz andere Bedeutung. Die Fassaden von Laravel sind im Wesentlichen statische Service-Locators . Die Benennung vermittelt das einfach nicht. Die gleiche Benennung für verschiedene Dinge erschwert auch Diskussionen über Architektur in Projekten, da die andere Partei möglicherweise etwas völlig anderes hinter diesem Namen erwartet.

Fazit

Lass uns zu Ende gehen. Ich könnte bald ein Follow-up darüber schreiben, welche Technologien ich heutzutage am liebsten benutze. Aber lassen Sie mich im Moment zusammenfassen, was wir gelernt haben:

Laravels Ansatz, alles so einfach wie möglich zu machen, ist gut. Es ist jedoch schwierig, miteinander auszukommen, wenn Ihre Apps weiterentwickelt werden. Ich bevorzuge großartige IDE-Unterstützung, stärkere Eingabe, echte Objekte und gute Technik. Ich könnte sogar zu Laravel zurückkehren, wenn ich eine kleinere App schreiben möchte.

Viele meiner Punkte sind nicht nur Laravels Schuld. Ich könnte die Teile tauschen, die ich nicht mag, zum Beispiel das ORM. Stattdessen wechsle ich einfach das Toolkit, wo die Standardeinstellungen besser zu meinen Anforderungen passen. Ich sehe keinen Sinn darin, ein Framework zu verwenden, in dem ich mehr Zeit damit verbringen muss, Fallen zu vermeiden, die für schlechtes Engineering entstehen, als meine App zu entwickeln. Andere Frameworks und Tools bieten besser gestaltete Standardeinstellungen und weniger Magie.

Also werde ich mich vorerst von Laravel verabschieden.

Vielen Dank für Ihre Zeit. Ich würde mich über eine nette Diskussion in den Kommentaren freuen und bin natürlich offen für Ihre Fragen und Vorschläge.

PS: Besonderer Dank geht an Marco Pivetta für das Korrekturlesen und zusätzliche Beiträge!

Bearbeiten 1. März 2019:

Da mein Artikel auf Reddit veröffentlicht wurde, habe ich ein Reddit-Konto erstellt, um einige Kommentare zu beantworten. Mein Konto ist nicht das, von dem der Artikel gepostet wurde, sondern dieses: //reddit.com/u/nschoellhorn

Bearbeiten 13. März 2019:

Wenn Sie so weit gelesen haben, können Sie auch mein Twitter-Profil überprüfen. Vielen Dank für Ihr anhaltendes Interesse an diesem Thema! Ich bin immer offen für produktive Diskussionen. Nehmen Sie Kontakt mit uns auf, entweder hier oder auf Twitter.