Une nouvelle approche dans Laravel permet de transformer les attributs stockés en base de données en objets métiers riches, ce qui simplifie la logique applicative tout en conservant une structure de stockage propre et claire. En implémentant l’interface CastsAttributes, il devient possible de définir comment un attribut est converti en objet lors de la lecture ou de l’écriture. Par exemple, une valeur monétaire peut être automatiquement encapsulée dans un objet MoneyValueObject contenant le montant et la devise, puis réinjectée comme tableau lors de la sauvegarde  .

Cette technique permet de créer des objets encapsulant données et comportements, tels que des méthodes de formatage. L’utilisation d’objets métiers facilite l’ajout de logique spécifique directement au sein de ces objets, tout en gardant les modèles Laravel simples et alignés sur la base de données  .

En complément, Laravel propose aussi d’utiliser le mécanisme Attribute avec la méthode protégée casts() (ou, dans les versions précédentes, la propriété $casts) pour des conversions plus classiques, comme transformer un champ JSON en un objet Address quand il est lu, et gérer la réécriture de ses sous-attributs lors de la sauvegarde  . Les types de conversion intégrés incluent notamment array, boolean, decimal, datetime, collection, AsStringable, ou encore AsUri pour obtenir des instances d’Illuminate\Support\Uri  .

Laravel étend également le casting JSON avec AsArrayObject et AsCollection, qui permettent respectivement d’avoir des objets modifiables (type ArrayObject) ou de bénéficier des puissantes fonctionnalités des collections Laravel sur les attributs JSON. Cela rend la manipulation d’attributs complexes beaucoup plus fluide et expressive  .

La version la plus récente, Laravel 11, introduit une nouvelle méthode casts() dans les modèles, qui peut remplacer ou coexister avec la propriété $casts. Cette évolutivité offre davantage de flexibilité, notamment la possibilité d’utiliser des méthodes statiques sur les casters intégrés pour une syntaxe plus lisible et expressive  .

Voici un exemple en PHP pour illustrer ce principe, sans copier l’article source mais en s’en inspirant. Supposons un attribut total_amount devant renvoyer un objet Money avec montant et devise, et stocker ces informations dans deux colonnes séparées :

<?php
namespace App\Casts;
 
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use App\ValueObjects\Money;
 
class MoneyCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes): Money
    {
        return new Money(
            (float) ($attributes['amount'] ?? 0),
            $attributes['currency'] ?? 'EUR'
        );
    }
 
    public function set($model, string $key, $value, array $attributes): array
    {
        return [
            'amount' => $value->amount,
            'currency' => $value->currency,
        ];
    }
}

Puis dans le modèle Laravel :

<?php
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use App\Casts\MoneyCast;
 
class Invoice extends Model
{
    protected function casts(): array
    {
        return [
            'total_amount' => MoneyCast::class,
        ];
    }
}

Dans ce contexte, lorsqu’on accède à $invoicetotal_amount, on obtient un objet Money avec des méthodes comme formatted() ou amount. En le modifiant, Laravel se charge de sauvegarder amount et currency séparément, selon la logique définie dans MoneyCast.

Cette approche présente des innovations intéressantes : elle renforce l’expressivité et l’encapsulation du code, en isolant la logique métier dans des objets dédiés. Elle améliore la maintenabilité en limitant la duplication de code de transformation. Toutefois, elle ajoute une certaine complexité : il faut créer des classes supplémentaires (casts, value objects), maîtriser l’interface CastsAttributes, et veiller à bien gérer la sérialisation/deserialisation des attributs. De plus, pour les simples conversions (comme boolean, datetime), cela peut être excessif comparé à l’usage direct de $casts.

En conclusion, Laravel enrichit son système de cast avec des solutions allant du plus simple ($casts / Attribute) aux plus sophistiqués (CastsAttributes, AsCollection, AsUri, AsStringable), offrant un large spectre selon les besoins de complexité métier. L’utilisation judicieuse de ces mécanismes permet à la fois de garder des modèles simples, de structurer la logique métier dans des objets dédiés, et d’améliorer la lisibilité du code. Pour les cas standards, les types primitifs suffisent ; dès qu’on souhaite encapsuler du comportement ou du polymorphisme, les casts personnalisés ouvrent de nouvelles possibilités.

Sources :