Laravel News hat ein Tutorial zur neuen First-Party-Bildverarbeitung von Laravel 13.20 veröffentlicht, die über die Komponente Illuminate\Image in Pull Request #59276 im Repository laravel/framework eingeführt wurde. Zuvor mussten Entwickler für Aufgaben wie das Skalieren von Avataren oder die Konvertierung von Uploads nach WebP auf Drittanbieter-Pakete zurückgreifen.
Die Treiber GD und Imagick basieren auf Intervention Image v4, das separat per `composer require intervention/image:^4.0` installiert wird. Standardmäßig nutzt Laravel GD; Imagick lässt sich global über die Konfiguration `images.default` oder pro Aufruf mit `usingImagick()` bzw. `using('imagick')` aktivieren. Fehlt Intervention Image, wirft Laravel eine `ImageException` mit Hinweis auf das fehlende Paket.
Bilder lassen sich aus vielen Quellen laden: `$request->image('avatar')` für Uploads (liefert null, wenn das Feld fehlt oder keine Datei ist), oder über die Facade-Methoden `Image::fromPath()`, `fromUrl()`, `fromStorage()`, `fromBytes()` und `fromBase64()` sowie die Kurzform `Storage::disk('s3')->image('pfad')`.
Jede Transformation liefert eine neue, unveränderliche `Image`-Instanz zurück; verarbeitet wird erst, wenn eine Ausgabe wie `store()`, `toBytes()` oder `width()` angefordert wird. So lässt sich ein Basisbild aufbauen (z. B. mit `orient()`, das anhand von EXIF-Daten automatisch dreht) und in mehrere Varianten verzweigen, etwa ein Thumbnail via `cover(300, 300)` und eine Anzeigegröße via `scale(width: 1600)`, ohne dass sich die Zweige gegenseitig beeinflussen.
Fünf Methoden zur Größenänderung stehen bereit: `cover($w, $h)` schneidet auf exakte Maße zu (ideal für Avatare); `contain($w, $h, $hintergrund)` passt das Bild mit optionalem Rand ein; `scale($w, $h)` skaliert proportional und vergrößert nie (intern `scaleDown()` von Intervention); `resize($w, $h)` erzwingt exakte Maße und kann verzerren; `crop($w, $h, $x, $y)` schneidet einen Bereich an gegebener Position aus.
Weitere Anpassungen sind `rotate()`, `blur()` (0–100, Standard 5), `sharpen()` (0–100, Standard 10), `grayscale()`, `flip()`/`flipVertically()` und `flop()`/`flipHorizontally()`. Für die Formatkonvertierung stehen `toWebp()`, `toJpg()`, `toPng()`, `toGif()`, `toAvif()` und `toBmp()` zur Verfügung, kombiniert mit `quality()` (1–100) für verlustbehaftete Formate. Die Kurzmethode `optimize()` konvertiert standardmäßig zu WebP mit Qualität 70, akzeptiert aber auch eigenes Format und Qualität, z. B. `optimize('avif', 60)`. Unterstützte Eingabeformate sind JPEG, PNG, GIF, BMP und WebP.
Die Speicherung orientiert sich an der UploadedFile-API von Laravel: `store()`, `storeAs()` und `storePublicly()` schreiben auf ein konfiguriertes Laufwerk und setzen automatisch die passende Dateiendung für das Ausgabeformat. Für Rohdaten stehen `toBytes()`, `toBase64()` und `toDataUri()` bereit, dazu die Inspektionsmethoden `width()`, `height()`, `dimensions()`, `mimeType()` und `extension()`.
Ein Beispiel-Controller für Avatare validiert den Upload und verkettet dann `orient()->cover(512, 512)->optimize()->storePublicly('avatars', disk: 's3')`. Ein Beispiel für responsive Varianten iteriert über die Breiten 480, 960 und 1440 mit `scale(width: $width)` — da `scale()` nie vergrößert, bleibt ein 900px breites Original auch in der 1440er-Iteration bei 900px.
Die `Image`-Klasse nutzt Laravels `Conditionable`-Trait, sodass `when()` und `unless()` Transformationen bedingt anwenden können. Erweiterungspunkte sind `Image::extend()` zum Registrieren eines eigenen Treibers sowie `Image::transformUsing()`, um eine einzelne Transformation pro Treiber zu überschreiben, ergänzt durch das Implementieren des Contracts `Illuminate\Contracts\Image\Transformation` für vollständig eigene Transformationen.
Wichtige Hinweise: Image-Instanzen können nicht serialisiert werden — der Versand an einen Queue-Job löst eine `ImageException` aus (stattdessen die Datei speichern und den Pfad übergeben); die Verarbeitung erfolgt lazy und wird nach der ersten Ausgabe zwischengespeichert; Fehler wie nicht unterstützte oder nicht dekodierbare Eingaben werfen einheitlich `ImageException`.