Entwickler, der außergewöhnliche CRM- und Laravel-Lösungen liefert

Als erfahrener Entwickler spezialisiere ich mich auf Laravel- und Vue.js-Entwicklung, die Implementierung von Vtiger CRM sowie auf vielfältige WordPress-Projekte. Meine Arbeit zeichnet sich durch kreative, dynamische und benutzerzentrierte Weblösungen aus, die individuell an die Bedürfnisse meiner Kunden angepasst werden.

Laravel ist ohne Zweifel eine der tragenden Säulen der modernen PHP-Entwicklung. Seine Popularität ist enorm, und das aus gutem Grund: eine niedrige Einstiegshürde, eine riesige Community, ein reichhaltiges Ökosystem und die Möglichkeit, sowohl einfache als auch mittelkomplexe Projekte schnell umzusetzen.

Laravel: Die Kehrseite der Popularität oder Worüber auf Konferenzen nicht gesprochen wird

Laravel: Die Kehrseite der Popularität oder Worüber auf Konferenzen nicht gesprochen wird
Laravel ist ohne Zweifel eine der tragenden Säulen der modernen PHP-Entwicklung. Seine Popularität ist enorm, und das aus gutem Grund: eine niedrige Einstiegshürde, eine riesige Community, ein reichhaltiges Ökosystem und die Möglichkeit, sowohl einfache als auch mittelkomplexe Projekte schnell umzusetzen. Die Integration mit Inertia.js ist ein Kapitel für sich – sie ermöglicht es, relativ schmerzfrei SPAs zu erstellen und dabei alle Vorzüge des Fullstack-Ansatzes zu nutzen. Doch wie jede Technologie hat auch Laravel seine „Leichen im Keller“ – Aspekte, die besonders bei großen und langlebigen Projekten für erhebliche Kopfschmerzen sorgen können. Sprechen wir genauer darüber.

  1. Der ewige Kampf um Typen
    Obwohl PHP aktiv in Richtung strikter Typisierung voranschreitet und Laravel versucht, Schritt zu halten, bleiben Typprobleme weiterhin aktuell. Ja, mit jeder neuen Version wird das Framework stärker typisiert, aber vom Ideal ist es noch weit entfernt. In Projekten, insbesondere dort, wo hohe Zuverlässigkeit und Vorhersagbarkeit des Codes entscheidend sind – wie beispielsweise in CRM-Systemen, wo Fehler bares Geld kosten können – führt die mangelnde Typstrenge periodisch zu unerwarteten Laufzeitfehlern, die eigentlich schon während der Entwicklung hätten abgefangen werden können.
  2. Inertia.js: Vorsicht, alles einsehbar!
    Inertia.js ist wirklich ein Durchbruch für Laravel in der Welt der SPAs. Aber es gibt eine Tücke, die Anfänger oft vergessen (und selbst erfahrene Entwickler tappen manchmal in diese Falle): Alle Daten, die Sie vom Backend an Frontend-Komponenten übergeben, werden auf Client-Seite sichtbar. Ein klassisches Beispiel aus der Entwicklung eines Onlineshops: Versehentlich wurde ein Array mit allen aktiven Gutscheincodes an die Produktkarten-Komponente übergeben. Scheinbar eine Kleinigkeit, aber ärgerlich. Daher die goldene Regel: Verwenden Sie immer Data Transfer Objects (DTOs) und filtern Sie sorgfältig, was an das Frontend geht. Besonders kritisch ist dies bei CRMs, wo Sie versehentlich interne Deal-Status oder persönliche Daten von Managern „durchsickern“ lassen könnten, die nicht für Kundenaugen bestimmt sind.
  3. Livewire: PHP-Magie statt JavaScript – nicht für jeden
    Die Livewire-Technologie wird als eine Möglichkeit positioniert, interaktive Oberflächen zu schreiben, ohne die vertraute Welt von PHP verlassen zu müssen. Für einfache Dinge wie die dynamische Filterung einer Liste oder die automatische Aktualisierung eines Blocks mag das praktisch sein. Aber ich persönlich bin mit Livewire nie richtig warm geworden. Die Idee selbst, in PHP etwas zu schreiben, das von Natur aus in JavaScript geschrieben werden sollte, erscheint mir etwas unnatürlich. Anstatt die volle Leistungsfähigkeit und Flexibilität des modernen JS (und seines Ökosystems) zu nutzen, muss man eine neue Syntax erlernen und sich mit Einschränkungen abfinden. Der Versuch, ein großes verschachteltes Formular mit vielen dynamischen Abhängigkeiten und kaskadierenden Filtern in Livewire zu implementieren, endete für mich in einem Fiasko. Fazit: Wenn die Wahl zwischen Inertia.js und Livewire steht, geht meine Stimme immer an Ersteres.
  4. Alleinige Führung: Besonderheiten der Entscheidungsfindung bei Laravel
    Verglichen mit Symfony, wo eine starke Community und kollektive Entscheidungsfindung vorherrschen, läuft es bei Laravel etwas anders. Die Schlüsselentscheidungen über die Weiterentwicklung des Frameworks, die Annahme von Pull Requests und die Einführung neuer Features liegen im Wesentlichen in den Händen einer einzigen Person – Taylor Otwell. Die Anzahl der Haupt-Contributor ist streng begrenzt, und dies sind hauptsächlich Mitarbeiter von Laravel. Dieser Ansatz führt zu ziemlich spezifischen Prozessen. Es ist nicht ungewöhnlich, dass kritische Fragen, die von der Community aktiv diskutiert werden, letztendlich von Taylor so entschieden werden, wie er es für richtig hält, selbst wenn dies den Argumenten der Mehrheit widerspricht. Ein prägnantes Beispiel ist das Problem mit truncate, das kaskadierend andere Tabellen in PostgreSQL betreffen konnte (Ticket #35157 auf GitHub). Lange Zeit weigerte sich Taylor, diesen Fehler zu beheben, und berief sich auf die Abwärtskompatibilität, obwohl das Problem nur einen Datenbanktreiber betraf und von einer globalen Abwärtskompatibilität keine Rede sein konnte.

Dieses Führungsmodell stößt bei einem Teil der Nutzer auf Unmut, und ehrlich gesagt ist so etwas in anderen großen Open-Source-Projekten selten anzutreffen. Taylor selbst erwähnte in einem Interview, dass er einen PR ablehnen und die gleiche Funktionalität dann selbst implementieren könnte, einfach um besser zu verstehen, wie sie funktioniert. Ein solcher Ansatz untergräbt meiner Meinung nach den Geist von Open Source und mindert die Motivation der Entwickler, zum Projekt beizutragen.

  1. Migrationen: Jahrelanger Schmerz
    Stellen Sie sich ein Projekt vor, das seit fünf, sieben oder sogar zehn Jahren aktiv entwickelt wird. Ständig werden neue Tabellen und Felder hinzugefügt, bestehende geändert. Wie viele Migrationen sammeln sich in dieser Zeit an? Hunderte, wenn nicht Tausende. Und nun stellen Sie sich vor, wie viel Zeit die Ersteinrichtung eines solchen Projekts von Grund auf in Anspruch nimmt, wenn jede dieser Migrationen nacheinander ausgeführt wird. Ein Werkzeug zum „Squashen“ (Zusammenfassen) von Migrationen gab es lange Zeit nicht im Laravel-Kern, obwohl dieses Problem in den meisten anderen Frameworks schon vor etwa fünfzehn Jahren gelöst wurde, beispielsweise durch eine einzige Schemadatei, die den aktuellen Zustand der Datenbank widerspiegelt.

Taylor widersetzte sich lange der Einführung eines solchen Mechanismus in den Kern und hielt ihn für überflüssig. Erst relativ kürzlich wurde die Möglichkeit eingeführt, eine SQL-Datei mit dem Datenbankschema zu generieren, die bei Migrationen anstelle der Ausführung aller alten Dateien verwendet wird. Aber erstens ist das nicht das Standardverhalten. Zweitens funktioniert es nicht so reibungslos wie in anderen Frameworks: Nach dem Erstellen einer Migration wird das Schema nicht automatisch aktualisiert, es sind zusätzliche Schritte erforderlich. Dabei verlassen sich viele Bibliotheken und Entwicklungswerkzeuge für Autovervollständigung und Code-Analyse auf eine aktuelle Schemadatei. In CRM-Systemen, wo die Datenstruktur sehr verzweigt sein und sich häufig ändern kann, wird die Verwaltung einer solchen Menge an Migrationen zu einer echten Qual.

  1. Middleware: Schichten-Wirrwarr
    Wie werden in Laravel alle Middlewares deklariert? Standardmäßig geschieht dies in der Datei bootstrap/app.php (in Versionen vor Laravel 11 war dies app/Http/Kernel.php). Für eine kleine Anwendung – kein Problem. Sobald das Projekt jedoch wächst, wird diese Datei zu einer schwer lesbaren Liste. Wie wendet man Middleware auf eine bestimmte Route oder Routengruppe an? Das kann in den Dateien routes/web.php oder routes/api.php geschehen. Allerdings wird es sehr schwierig, die Gesamtstruktur der Middleware-Anwendung zu durchschauen und zu verstehen, welche Schicht wo verwendet wird. Die Datei bootstrap/app.php (oder Kernel.php) bietet zwar eine klarere Übersicht über die Gruppen, erlaubt aber nicht, Middleware einzelnen Routen mit derselben Flexibilität zuzuweisen.

Neue Syntax in Laravel 11+ für bootstrap/app.php:

->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('group-name', [
        First::class,
        Second::class,
    ]);
    $middleware->prependToGroup('group-name', [
        Third::class,
        Fourth::class,
    ]);
})

Das verbessert die Situation für Gruppen zwar ein wenig, aber das grundlegende Problem der Lesbarkeit und des Managements von Schichten in großen Anwendungen bleibt bestehen. Fairerweise muss man sagen, dass in Symfony, wo es keine Middleware im eigentlichen Sinne gibt, sondern ein System aus Events und Listenern, die Logik der Anfrageverarbeitung noch undurchsichtiger und verwirrender sein kann.

  1. Caching: „Lösch einfach den Cache!“
    Das ist wohl eines der leidigsten Themen in Laravel. Schon zu Beginn meiner Arbeit mit dem Framework hat mich die universelle Antwort auf viele Probleme unglaublich genervt: „Lösch den Cache.“ Man macht irgendetwas, versteht nicht, warum es nicht funktioniert, sucht im Internet und findet dort: „php artisan cache:clear“, „php artisan config:clear“, „php artisan route:clear“, „php artisan view:clear“… Laravel hat mehrere Arten von Caches, und es ist nicht immer offensichtlich, welcher wofür zuständig ist und welcher gerade gelöscht werden muss.

Ja, dafür gibt es technische Gründe, und wer will, kann sich in die Feinheiten einarbeiten. Aber warum muss ich Zeit damit verbringen, die Caching-Besonderheiten des Frameworks selbst zu studieren, insbesondere in einer lokalen Umgebung? Ich erinnere mich an Tage, an denen ich stundenlang mit einem Problem gekämpft habe, das sich mit einem einzigen Befehl zum Löschen des Caches beheben ließ. Und es geht hier nicht um den Anwendungscache (Daten, die wir selbst cachen), sondern um den internen Cache des Frameworks – Konfigurationen, Routen, Views. Das Kernproblem ist, dass Laravel die Werte der Umgebungsvariablen (aus der .env-Datei) beim Ausführen von config:cache zwischenspeichert. Dieser Ansatz ist nicht nur während der Entwicklung unpraktisch, sondern widerspricht auch den Prinzipien der Twelve-Factor App, die eine strikte Trennung von Konfiguration und Code vorschreiben.

  1. Fehlender vollwertiger Message Bus „out of the box“
    In Laravel gibt es ein System von „Jobs“, das die Ausführung zeitversetzter Operationen ermöglicht. Das ist praktisch für den Versand von E-Mails, die Bildverarbeitung usw. Wenn man jedoch die Interaktion zwischen mehreren Microservices aufbauen muss, besonders wenn nicht alle davon in Laravel geschrieben sind, erweisen sich Jobs nicht als die beste Lösung. Das Job-System in Laravel kann man kaum als vollwertigen Message Bus im klassischen Sinne bezeichnen. Ja, das Framework ermöglicht es, ein Ereignis beispielsweise an RabbitMQ zu senden, aber man kann das Nachrichtenformat nicht flexibel steuern oder eine eigene Logik für die Arbeit mit Queues definieren, ohne erhebliche Anpassungen vorzunehmen. Für komplexe Szenarien der Interaktion zwischen Diensten muss man entweder Lösungen von Drittanbietern integrieren oder viel eigenen Code schreiben.
  2. JSON-Übersetzungen: Kein Recht auf Unterordner
    Laravel bietet zwei Möglichkeiten, Übersetzungsdateien zu speichern: als PHP-Arrays oder in JSON-Dateien. Wenn man PHP-Dateien wählt, kann man sie bequem in Unterordnern strukturieren, z. B. lang/en/user.php, lang/en/product.php. Das ist sehr praktisch für große, mehrsprachige Projekte. Wenn man sich jedoch aus irgendeinem Grund für JSON-Dateien für Übersetzungen entscheidet (z. B. für ein einfacheres Parsen im Frontend), muss man damit rechnen, dass alle Schlüssel für eine Sprache in einer einzigen Datei liegen müssen (lang/en.json). Die Möglichkeit, sie wie bei PHP-Dateien in logische Unterordner aufzuteilen, gibt es nicht. Das kann zu riesigen, schwer zu verwaltenden JSON-Dateien führen.
  3. Autovervollständigung in der IDE: Magie auf Kosten der Bequemlichkeit
    Die Debatten darüber, ob die Verwendung von Fassaden und „Magie“ in Frameworks gut oder schlecht ist, reißen nicht ab. Meine Meinung: Magie ist gut, wenn sie vorhersagbar ist und die Entwicklung nicht behindert. In Laravel führt „Magie“ jedoch oft dazu, dass man ohne zusätzliche Werkzeuge die Autovervollständigung in seiner IDE nicht voll nutzen kann. Damit die IDE versteht, dass User::create() eine solche Methode hat, muss man ein spezielles Plugin installieren (z. B. Laravel Idea für PhpStorm), da die statische Methode create() tatsächlich nicht in der Klasse User vorhanden ist, sondern „magisch“ über eine Fassade weitergeleitet wird. Wenn man auf Plugins verzichten möchte, muss man ausführlichere Konstruktionen wie User::query()->create() schreiben. Ähnlich verhält es sich mit der Autovervollständigung von Modellfeldern – ohne Plugins erkennt die IDE sie nicht.
  4. hasManyThrough: Das Rätsel der Argumente
    Ich habe diese Methode zur Definition von Eloquent-Beziehungen bewusst in einen eigenen Punkt aufgenommen. Egal wie viel ich mit Laravel geschrieben habe, ich habe es nie geschafft, mir die Reihenfolge der Argumente in hasManyThrough automatisch einzuprägen. Jedes Mal, wenn ich es verwenden muss, schaue ich in die Dokumentation. Warum man keine intuitivere Syntax machen konnte, zum Beispiel so etwas wie hasMany(Deployment::class)->through(IntermediateTable::class)->hasMany(Environment::class) (ein übertriebenes Beispiel zur Veranschaulichung der Idee), bleibt mir ein Rätsel.
  5. Traits, Traits überall
    Laravel verwendet Traits sehr intensiv. Sie sind buchstäblich überall. Einerseits ermöglicht dies die Wiederverwendung von Code und das Hinzufügen von Funktionalität zu Klassen. Andererseits erschwert es das Lesen und Verstehen des Quellcodes erheblich. Es kommt nicht selten vor, dass einige Traits andere Traits verwenden, diese wiederum weitere, und so weiter. Am Ende stehen wir vor dem sogenannten „Yo-Yo-Problem“, bei dem man, um die Logik einer Methode zu verstehen, durch eine Kette mehrerer Traits springen muss und versucht, diese ganze komplexe Struktur von Vererbung und Mixins im Kopf zu behalten.
  6. „Nachgiebigkeit“ der Modelle standardmäßig
    In Eloquent-Modellen sind standardmäßig nützliche Optionen wie strictMode und preventAccessingMissingAttributes (oder preventSilentlyDiscardingAttributes in neueren Versionen) deaktiviert. Sie müssen explizit im Service Provider oder im Basismodell aktiviert werden. Leider sind diese Funktionen in vielen Projekten, die zur Wartung kommen, nicht aktiviert. Wozu führt das? Zu einem sehr merkwürdigen Verhalten, wenn man versehentlich auf eine nicht existierende Eigenschaft eines Modells zugreift oder versucht, einem nicht existierenden Attribut einen Wert zuzuweisen, und Laravel dies ohne Fehler oder Warnungen „schluckt“ (abhängig von der jeweiligen Option und Version). Das kann Fehler verschleiern und zu schwer zu debuggenden Problemen führen, besonders wenn man sich versehentlich beim Namen eines Feldes bei der Massenzuweisung von Attributen in einem CRM vertippt hat, wo Datengenauigkeit entscheidend ist.

Fazit

Trotz der genannten Nachteile war und ist Laravel ein mächtiges Werkzeug für die Webentwicklung. Seine Stärken überwiegen oft die Schwächen, insbesondere bei bestimmten Projekttypen. Es ist jedoch wichtig, auch die „Schattenseiten“ zu kennen, um auf mögliche Schwierigkeiten vorbereitet zu sein und fundierte Entscheidungen bei der Wahl des Technologie-Stacks oder bei der langfristigen Wartung eines Laravel-Projekts treffen zu können. Das Verständnis dieser Nuancen hilft Ihnen, qualitativ hochwertigeren Code zu schreiben und viele Fallstricke zu vermeiden.