Fünf Veränderungen für ein optimiertes Laravel

PHP Framework Laravel optimieren – eine Idee
2 Kommentare

Laravel ist ein immer beliebter werdendes PHP Framework. Doch auch gute Dinge haben Luft nach oben und aus diesem Grund schlägt Jason McCreary fünf Ideen vor, mit denen man Laravel weiterhin optimieren könnte.

In letzter Zeit haben mich immer mehr Leute gefragt, was ich an Laravel verändern würde. Bevor ich darauf antworte, möchte ich erwähnen, dass ich Laravel liebe! Nicht nur, dass ich es genossen habe, Laravel für alle meine Projekte in den letzten sechs Jahren benutzt zu haben. Laravel hat es mir zudem ermöglicht, in Vollzeit an meinen eigenen Projekten zu arbeiten und die Welt zu bereisen.

Hier handelt es sich also nicht um ein Fractal of Bad Design. Nein, das hier ist ein Vorschlag für Ideen für die zukünftigen Versionen des Frameworks.

Als Schöpfer von Shift sind diese Vorschläge natürlich auch durch die Wartbarkeit motiviert. Sie werden aber auch von jemanden motiviert, der Produkte herstellt. Insbesondere Produkte, die letzten Endes fehlschlugen, hauptsächlich aus dem Grund, dass sie veraltet waren.

In der Beratung oder bei der Teilnahme an Konferenzen höre ich mir auch die Fragen der Entwickler an. Ich achte auf die Aspekte von Laravel, die sie mögen oder nicht mögen.

Von Zeit zu Zeit ist eine revolutionäre Veränderung erforderlich. Dies bietet die Chance, Ziele zu überdenken. Eines der primären Ziele von Laravel ist die Developer Experience. Und hier verbessern Wartbarkeit, Neuheiten und Zugänglichkeit die Erfahrung der Entwickler.

Mit dem all dem im Hinterkopf, sind das hier die fünf wichtigsten Dinge, die ich an Laravel ändern würde.

Mass Assignment

Das ist wahrscheinlich eines der wenigen (oder einzigen) Features, das es nicht in Laravel gibt. Das Ziel der Massenzuweisung ist es, Modelle vor der Einspeisung unerwarteter Werte zu schützen, die häufig aus den Anforderungsdaten kommen. Aus einer sicherheitstechnischen Perspektive ist die Massenzuweisung wichtig. Von der Feature-Perspektive aus gesehen, funktioniert sie. Es ist die Adaptionsperspektive, an der es noch mangelt.

Die meisten Entwickler stellen alle Modellspalten auf fillable ein. Oder noch schlimmer, sie deaktivieren die Massenzuweisung komplett, indem sie ihre Modelle ungeschützt (unguarding) lassen. Beides widerspricht dem Zweck der Massenzuweisung.

Ich bin mir nicht sicher, ob das die Lösung ist. Ein Ansatz wäre die Neuimplementierung des Features. Rails tat dies durch die Verwendung starker Parameter.

So wie es aussieht, würde ich es vorziehen, es zu entfernen. Man sollte Entwickler dazu zwingen, die Verantwortung für die korrekte Validierung und Zuweisung der Modelldaten zu übernehmen.

Weniger Hilfe

Laravel ist außerordentlich hilfreich. Frühe Versionen von Laravel 5 schienen mehr und mehr Helfer hinzuzufügen. Sogar Helfer, die lediglich unterliegende Facades umbrechen.

Es ist schwierig gegen etwas „Hilfreiches“ zu argumentieren – immerhin verbessern diese Helfer die Erfahrung der Entwickler. Es kann jedoch auch beeinträchtigend sein, wenn etwas übermäßig hilfreich ist.

Ich finde viele Entwickler vor, die Helfer verwenden, selbst wenn die zugrunde liegenden Objekte leicht verfügbar sind. Die Helfer werden dann eingesetzt, weil es einfach leicht ist. Ein Entwickler kann mit einem Helfer auch auf Objekte zugreifen, auf die er sonst keinen Zugriff hätte. Dadurch werden wichtige Grenzen verwischt. Grundlegende Designaspekte, wie Kopplung und Kohäsion sowie die MVC-Architektur, gehen verloren.

Neuere Versionen von Laravel haben den Einsatz von Helfern eingeschränkt. Zum Beispiel wurden alle Array– und String-Hilfsfunktionen entfernt und stattdessen wird das Klassenobjekt verwendet.

IT Security Summit 2020

Zero Trust – why are we having this conversation?

mit Victoria Almazova (Microsoft)

Digitaler Ersthelfer

mit Martin Wundram (DigiTrace GmbH)


Ich möchte, dass das auch weiterhin gemacht wird und dass es in einigen Fällen sogar noch intensiviert wird. Ein Anfang könnte die Entfernung der Authentifizierungs- und Anfragehelfer sein. Diese führen zu einigen der schlimmsten Verstöße. Dennoch ist die Änderung einfach, da alternative Objekte und Muster leicht verfügbar sind. Dadurch ergibt sich oftmals besserer Code.

Sehen wir uns die folgende Controller-Aktion an:

public function store()
{
    $user = User::createWithCheckout();
    $order = Order::createWithUser($user);

    return redirect()->route('order.show', $order->id);
}

Oberflächlich betrachtet, scheint dieser Code in Ordnung zu sein. Die Probleme kommen erst in den unteren Ebenen des Codes zum Vorschein. In diesem Fall greift das Modell stark auf die auth() und request() Helfer (oder Facade) zu.

public function createWithCheckout()
{
    if (Auth::check()) {
        return auth()->user();
    }

    return User::create([
      'email' => request('email'),
      'password' => Hash::make('...')
    ]);
}

Dies geht wahrscheinlich weit über ihre ursprüngliche Absicht hinaus. Aber als Entwickler können wir uns einfach nicht anders helfen (Wortspiel).

Wenn wir darüber nachdenken, erreichen wir tatsächlich mehrere Schichten in unserem Application Stack. Den ganzen Weg vom Modell bis zur Anfrage. Von einer MVC-Perspektive aus gesehen, überschreitet dies Grenzen.

Es gibt bereits Alternativen, die MVC respektieren und den Code entkoppeln. Laravel injiziert das requestObjekt in alle Controller-Aktionen. Dieses Objekt hat auch Zugriff auf den authentifizierten Benutzer.

public function store(Request $request)
{
    $user = User::createWithCheckout($request);
    $order = Order::createWithUser($user);

    return redirect()->route('order.show', $order->id);
}

Wir können dieses request-Objekt an die untere Ebene des Codes weitergeben. Möglicherweise sogar Typ-Hinting mit einem Formular-Anforderungsobjekt, um die verfügbaren Anforderungsdaten zu kommunizieren.

public function createWithCheckout(Checkout $request)
{
    if ($request->user()) {
        return $request->user();
    }

    return User::create([
      'email' => $request->input('email'),
      'password' => Hash::make('...')
    ]);
}

Das Entfernen dieser Helfer würde auf kurze Sicht schmerzlich sein, aber langfristig gesehen wäre es ein Gewinn für die Code-Qualität der Community.

Eine gute Sache

Ähnlich wie Helfer und Facades mehr als eine Möglichkeit bieten, Dinge zu schreiben, bietet Laravel viele überlappende Komponenten. Obwohl jede von ihnen subtile Unterschiede aufweisen, die sie besser an die Bedürfnisse verschiedener Entwickler anpassen, könnten sie wahrscheinlich optimiert werden. Einige Beispiele hierfür sind Mailables versus Notifications, Events versus Listeners versus Observers und Gates versus Policies.

Das Problem ist die Überlappung, die auf beiden Seiten zur Aufblähung führt. Aus der Sicht eines Entwicklers ist es unklar, was in welchen Szenarien verwendet werden soll. Aus der Sicht eines Maintainers ist es zusätzlicher Code, der unterstützt werden muss.

Nochmals, ich denke nicht, dass diese entfernt werden sollten. Sie alle bieten ein bestimmtes Stück Funktionalität an. Ich schlage stattdessen vor, dass man einen genauen Blick auf die Komponenten wirft, mit einem Fokus auf die Konsolidierung. Beispielsweise könnten Mailables mit Notifications zusammengeführt werden. Somit gibt es also eine Komponente, mit der man dies gut machen kann.

Legacy Laravel

Es gibt einige Muster und Strukturen innerhalb von Laravel, die bereits länger mit dabei sind. Wir haben uns an sie gewöhnt. Modernere Frameworks aber stellen sie bloß. Ich befürworte nicht eine leere Verzeichnisstruktur. Ich schätze sogar die Struktur, die Laravel Out-of-the-Box anbietet.

Aber ich denke, dass es Möglichkeiten gibt, sie zu optimieren. Dabei könnte etwas von dem Fett weggeschnitten werden. Es gibt insbesondere zwei Muster innerhalb einer Laravel-Anwendung, die ich für etwas veraltet halte. Anders ausgedrückt, diese sind relativ niedrig stufig im Vergleich zum Rest von Laravel. Dies sind der Kernel und die Service Provider. Shifts Analysen zeigen, dass diese Dateien nicht häufig geändert werden. Wenn sie geändert werden, sind diese Änderungen oft simpel, wie zum Beispiel das Binden eines Singletons oder die Registrierung einer benannten Middleware.

Ich denke, diese könnten konsolidiert werden und in ein anderes, modernes Muster übertragen werden, dem Laravel bereits folgt. Wenn wir in den routes-Ordner schauen, gibt es mehrere Dateien, um HTTP-Routen, Konsolenbefehle und Broadcast-Kanäle zu registrieren. Ähnlich könnte Laravel eine middleware.php-Datei für die Registrierung von benutzerdefinierter Middleware und eine container.php-Datei für die Anbindung von Klassen an den Container haben. Ich würde dann den routes-Ordner in etwas wie bindings umbenennen, um die neue Absicht der Dateien darin besser zu kommunizieren.

Durch die Verlagerung dieser Verantwortungen in einfache Konfigurationsdateien würde dies die Notwendigkeit für die Kernel-Dateien sowie die Service Provider beseitigen. So wird auch ein optimierter app-Ordner bereitgestellt, der nur den Http-Ordner und das -Modell enthält.

Erweiterte Konfiguration

Die häufigste Änderung in Laravel stellen die Konfigurationsdateien dar. Diese ändern sich nicht nur zwischen den Releases, sondern auch in den wöchentlichen Patches. Als solches ist es fast unmöglich, die Konfigurationsdateien auf dem neuesten Stand zu halten. Dies ist etwas, das ich aus erster Hand mit Shift gelernt habe und wiederholt den Code umschrieb, um die Wartung der Konfigurationsdateien zu erleichtern. Letztendlich halten die Entwickler diese Dateien nicht auf dem neuesten Stand oder befolgen Best Practices, die die Wartung erleichtern. Während ich also froh bin, weiterhin bei Shift zu helfen, sehe ich eher eine Veränderung in Laravel.

Ich denke, dass die Lösung darin besteht, die Konfigurationsdateien komplett zu entfernen. Stattdessen sollten man die Konfigurationsoptionen innerhalb des Konfigurationsabschnittes dokumentieren und sie in der Dokumentation kontextbezogener referenzieren. Man sollte es Entwicklern erlauben, diese über die verfügbaren ENV-Dateien anzupassen. Dies würde einen Großteil der Anwendungsfälle abdecken und die Erfahrung von Developer verbessern, indem der Aspekt der Wartung entfernt wird. Um feinere Anpassungen zu ermöglichen, führt man eine einzelne configuration.php-Datei ein (unterhalb des neuen bindings-Ordners).

Ähnlich wie bei Laravel 4.2 oder ähnlich der Konfiguration in Tailwind konnten die Entwickler die Kernkonfiguration erweitern und ihre eigenen Anpassungen hinzufügen. Dies würde keine Auswirkungen auf die Leistung haben, da es immer noch eine einzige Array-Zusammenführung wäre. Tatsächlich könnte es sogar die Leistung für eine nicht gecachte Konfiguration verbessern, da es nur eine Datei zu laden gäbe, anstatt den config-Ordner zu scannen.

Abschließende Überlegungen

Wenn Ihr euch in einer Laravel-Anwendungsstruktur umsehen wollt, die diese vorgeschlagenen Änderungen übernimmt, habe ich ein streamlined-Laravel-Repository einer Laravel 6.5.2-Anwendung mit diesen vorgeschlagenen Änderungen erstellt. Alle Änderungen erfordern eine Modifikation des Frameworks. Dies beinhaltet eine unbekannte Menge an Arbeit und würde definitiv auch das Brechen von Änderungen beinhalten. Als solches sind sie nicht etwas, das ich in nächster Zeit erwarte. Vielleicht Laravel 8…

In der Zwischenzeit stimmt mir bitte auf Twitter zu oder widersprecht mir, und schlagt zudem Eure eigenen Änderungen vor.

Dieser Artikel erschien zuerst im Englischen, geschrieben von Jason McCreary auf seinem Blog. Übrigens könnt ihr Jason McCreary live erleben: Auf der International PHP Conference 2020 Berlin hält er einen Laravel-Workshop.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Abonnieren
Benachrichtige mich bei
guest
2 Comments
Inline Feedbacks
View all comments
Fabian

Irgendwie absurd dass Laravel die selben Fehler wiederholt hat, wie damals Zend Framework. Wir hatten ja nix, sondern nur Zend_Registry, was nix anderes war als ein Global-State-Objekte. Dann hat ZF ab 2011 alles richtig gemacht und komplett auf Dependency Injection gesetzt. 2020 merkt man bei Laravel, dass die zugrunde liegenden Konzepte schlecht sind (Global-State-Objekte).

Fabian

… und der Code in Projekten dadurch schlecht wird, weil man ggf. gegen die SOLID-Prinzipien verstößt.

Sorry, aber das war schon immer eine schwerwiegende Designschwäche des Frameworks.

X
- Gib Deinen Standort ein -
- or -