Alle werden über das AI SDK und die Passkeys reden, und klar, die machen in der Demo was her. Aber der Teil von Laravel 13, der mir nicht aus dem Kopf geht, ist deutlich weniger glamourös: Das Release liefert zwei widersprüchliche Antworten auf dieselbe Frage, nämlich wo deine Konfiguration eigentlich leben soll. Du kannst einen Queued Job jetzt mit einem #[WithQueue]-Attribut dekorieren, das Connection, Queue-Name, Tries und Timeout direkt an der Klasse deklariert. Oder du lässt die Klasse blank und deklarierst all das in einem Service Provider mit Queue::route(), das Job-Klassen an einer zentralen Stelle auf Connections und Queues mappt. Dieselben Stellschrauben, zwei Zuhause, und das Framework hat keine Meinung dazu, welches du wählst. Also biete ich meine an.
<?php
// inside AppServiceProvider::boot()
use App\Jobs\GeneratePdfReport;
use App\Jobs\ProcessPodcast;
use Illuminate\Support\Facades\Queue;
Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'media');
Queue::route(GeneratePdfReport::class, connection: 'sqs', queue: 'exports');Zuerst aber Anerkennung, wo sie hingehört: Die Upgrade-Story ist wirklich gut. Wenn du auf PHP 8.3 oder neuer bist, verspricht das Core-Team keine Breaking Changes an deinem Anwendungscode und ein Upgrade, das in eine Kaffeepause passt. Attribute funktionieren jetzt an über fünfzehn Stellen im Framework, Eloquent-Modelle können also #[Table], #[Fillable] und #[Hidden] tragen statt eines Stapels protected Properties, und alles davon ist Opt-in. Deine bestehenden Modelle laufen unangetastet weiter. Diese Zurückhaltung verdient ein Nicken, denn sie bedeutet, dass die interessanten Entscheidungen in diesem Release stilistische sind, die du bewusst treffen darfst, statt Migrationen, die dir eine Deadline aufzwingt.
Hier ist meine Regel, und ich würde sie in jedem Code Review verteidigen: Deployment-Entscheidungen gehören in den Provider, Domänenform an die Klasse. Auf welcher Connection ein Job läuft, ob PDF-Exporte auf SQS landen, während Media-Processing auf Redis bleibt, das ändert sich, wenn sich deine Ops-Situation ändert, nicht wenn sich deine Businesslogik ändert. Wer das zentral über Queue::route() routet, fasst an dem Tag, an dem die Exporte auf ein anderes Backend umziehen, einen Provider an und null Job-Klassen, und jede Dispatch-Stelle bleibt ein schlichtes ProcessPodcast::dispatch($podcast). Die Fillable-Liste eines Modells dagegen gehört untrennbar dazu, was das Ding ist. Sie gehört an die Klasse, und die Attribut-Syntax lässt sie endlich wie eine Deklaration lesen statt wie Ballast.
Das ehrliche Gegenargument ist Auffindbarkeit, und ich habe mir daran in anderen Ökosystemen schon die Finger verbrannt. Wenn jemand Neues im Team GeneratePdfReport öffnet und nichts darüber sieht, wo der Job läuft, muss die Person wissen, dass der Provider existiert. Wer sich je durch eine Spring-Codebasis gewühlt hat auf der Suche nach der einen XML-Datei, die alles erklärt, kennt diesen Schmerz, und die Attribut-Variante hält die ganze Geschichte in einer einzigen Datei greppbar. Ich lande trotzdem bei zentralem Routing für Infrastruktur, weil der Failure Mode verstreuter Konfiguration schlimmer ist: Fünfzig Job-Klassen, die jeweils ihren eigenen Connection-String tragen, sind fünfzig Stellen, die du beim Queue-Umzug vergessen kannst, und ich habe genau das schon einen Freitags-Incident produzieren sehen, bei dem die Hälfte der Jobs an den alten Cluster ging.
Das Sleeper-Feature des Releases ist für mich Cache::touch(). Die TTL eines Keys zu verlängern hieß bisher, den Wert zu lesen und zurückzuschreiben, zwei Netzwerk-Roundtrips plus Serialisierung, nur um einen Timestamp anzustupsen. Jetzt schickt Cache::touch('user_session:123', 3600) an Redis ein einzelnes EXPIRE, spricht Memcached mit dessen nativem TOUCH an und führt auf dem Datenbank-Treiber ein schlichtes UPDATE aus. Wenn du je einen Sliding-Window-Rate-Limiter gebaut oder eine heiße Dashboard-Metrik warm gehalten hast, weißt du, wie oft dieses Muster vorkommt. Eine kleine API, die eine Sache tut und sauber auf das abbildet, was jeder Store tatsächlich kann. Mehr davon, bitte.
Die schickeren Punkte sind auch real, ich bezweifle nur, dass sie deinen täglichen Diff so umformen werden wie die Konfigurationsfrage. Das Laravel AI SDK wird parallel zum Release stable, mit PostgreSQL und pgvector bekommst du also whereVectorSimilarTo() direkt im Query Builder, mit Embedding-Generierung angebunden an OpenAI, Anthropic oder Ollama. Passkey-Login via WebAuthn steckt jetzt in Fortify und den Starter Kits, und da der Private Key das Gerät des Users nie verlässt, ist Credential Stuffing gegen deinen Login-Endpoint keine Bedrohung mehr, die sich für Angreifer lohnt. Dazu kommen der Reverb-Datenbank-Treiber, um Websockets über MySQL oder PostgreSQL zu skalieren ohne eine Redis-Box, Http::pool() mit einer Default-Concurrency von 2 statt seriellem Kriechen, und Teams kehren in die Starter Kits zurück mit tab-isoliertem Routing, damit ein Teamwechsel in einem Browser-Tab keinen anderen korrumpiert.
Eine Randnotiz zu Attributen, weil leicht zu übersehen ist, wie weit PHP hier gekommen ist. Go-Entwickler stopfen seit einem Jahrzehnt Metadaten in Struct Tags, rohe Strings, die der Compiler bis zur Laufzeit fröhlich ignoriert. PHP-Attribute sind echte Klassen mit Named Arguments, typisiert, autovervollständigt, refactorbar. Dass Laravel sie quer durchs Framework umarmt, ist weniger ein Laravel-Feature als das Ökosystem, das endlich einen Scheck einlöst, den die Sprache mit PHP 8.0 ausgestellt hat. Es hat gedauert, aber die Syntax, die wir bekommen, ist schöner als das, worauf sich einige angeblich modernere Sprachen geeinigt haben.
Das ist also meine Linie: Provider für alles, was eine Ops-Entscheidung ändern kann, Attribute für alles, was das Domänenobjekt selbst definiert. Aber ich kenne Teams, die diese Trennung rundweg ablehnen und sich auf einen Stil festlegen werden, nur um Reviews einfach zu halten, und ich kann sie nicht falsch nennen. Wo landest du? Routest du Jobs zentral, dekorierst du sie mit #[WithQueue], oder schreibst du eine Pint-Regel, die verbietet, was auch immer die Diskussion in eurem Standup verloren hat? Schreib es mir unten, besonders wenn du schon beides in einer Codebasis gemischt hast.




Kommentare
Noch keine Kommentare — schreib den ersten.
Starte die Diskussion
Kein Konto, kein Passwort nötig — gib einfach deine E-Mail-Adresse ein, wir senden dir einen einmaligen Anmelde-Link. Beim ersten Mal bist du damit automatisch angemeldet.
Deine Bewertung wird nach der Anmeldung automatisch übernommen.
Schau in dein Postfach
Wir haben einen Anmelde-Link an … gesendet. Öffne ihn auf diesem Gerät — dieser Tab meldet dich automatisch an.
Nichts angekommen? Wirf einen Blick in den Spam-Ordner — und markiere die Mail dort als „Kein Spam“, dann landet sie künftig direkt im Postfach.