Créez des casts personnalisés en implémentant l’interface CastsAttributes. Découvrez également des patterns avancés comme Value Object, casts entrants (inbound) et Castables.
Les casts Eloquent permettent de convertir la valeur brute récupérée en base en un type PHP et d’effectuer l’opération inverse lors de la sauvegarde. Ils sont définis via la méthode casts.
Voici la liste des casts fournis en standard par Laravel.
Cast
Description
integer / int
Convertit en entier
float / double
Convertit en nombre à virgule flottante
string
Convertit en chaîne de caractères
boolean / bool
Convertit en booléen (inclut 0/1)
array
Chaîne JSON ↔ tableau PHP
collection
Chaîne JSON ↔ instance Collection
object
Chaîne JSON ↔ instance stdObject
datetime
Chaîne ↔ instance Carbon
immutable_datetime
Chaîne ↔ instance CarbonImmutable
date
Chaîne ↔ Carbon (sans heure)
timestamp
Chaîne ↔ timestamp UNIX
encrypted
Chiffré à la sauvegarde, déchiffré à la lecture
hashed
Haché à la sauvegarde (à combiner avec un cast en lecture seule)
AsStringable::class
Chaîne ↔ objet Stringable
AsArrayObject::class
JSON ↔ instance ArrayObject
AsCollection::class
JSON ↔ instance Collection
AsArrayObject et AsCollection sont implémentés en interne dans Laravel comme des casts personnalisés, afin de permettre la modification directe d’offsets spécifiques du tableau.
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.phpinterface CastsAttributes{ /** * Convertit la valeur brute de la BDD en valeur PHP (en lecture) * * @param array<string, mixed> $attributes Toutes les valeurs d'attributs du modèle */ public function get(Model $model, string $key, mixed $value, array $attributes); /** * Convertit la valeur PHP en une forme stockable en BDD (en écriture) * * @param array<string, mixed> $attributes Toutes les valeurs d'attributs du modèle */ public function set(Model $model, string $key, mixed $value, array $attributes);}
L’argument $attributes contient tous les attributs du modèle, ce qui rend possible une conversion s’étendant sur plusieurs colonnes (voir le pattern Value Object ci-dessous).
Regroupez les deux colonnes address_line_one et address_line_two dans un Value Object Address.
<?phpnamespace App\ValueObjects;use Illuminate\Contracts\Support\Arrayable;class Address implements Arrayable, \JsonSerializable{ public function __construct( public readonly string $lineOne, public readonly string $lineTwo, ) {} public function toArray(): array { return [ 'line_one' => $this->lineOne, 'line_two' => $this->lineTwo, ]; } public function jsonSerialize(): array { return $this->toArray(); }}
<?phpnamespace App\Casts;use App\ValueObjects\Address;use Illuminate\Contracts\Database\Eloquent\CastsAttributes;use Illuminate\Database\Eloquent\Model;use InvalidArgumentException;class AsAddress implements CastsAttributes{ /** * Construit un objet Address à partir de plusieurs colonnes */ public function get( Model $model, string $key, mixed $value, array $attributes, ): Address { return new Address( $attributes['address_line_one'], $attributes['address_line_two'], ); } /** * Décompose l'objet Address en un tableau par colonne * * @return array<string, string> */ public function set( Model $model, string $key, mixed $value, array $attributes, ): array { if (! $value instanceof Address) { throw new InvalidArgumentException('The given value is not an Address instance.'); } return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; }}
Si set retourne un tableau, Eloquent utilise les clés comme noms de colonnes et sauvegarde chaque valeur dans la colonne correspondante. Pour un cast à une seule colonne, retournez une chaîne ou un entier.
L’application au modèle et l’utilisation sont les suivantes :
<?phpnamespace App\Models;use App\Casts\AsAddress;use Illuminate\Database\Eloquent\Model;class User extends Model{ protected function casts(): array { return [ 'address' => AsAddress::class, ]; }}
$user = User::find(1);// Récupéré comme objet Addressecho $user->address->lineOne;// Modifier le Value Object le reflète automatiquement en BDD à la sauvegarde$user->address = new Address('123 Main St', 'Apt 4B');$user->save();
Les attributs convertis en Value Object sont mis en cache par Eloquent. Accéder deux fois au même attribut retourne la même instance d’objet.Pour désactiver la mise en cache, ajoutez la propriété $withoutObjectCaching à la classe de cast.
class AsAddress implements CastsAttributes{ public bool $withoutObjectCaching = true; // ...}
Cast qui effectue la conversion uniquement lors de l’écriture en BDD, sans conversion lors de la lecture. Implémentez l’interface CastsInboundAttributes.Le cas d’usage typique est le hachage. Convertir uniquement à la sauvegarde d’un mot de passe ou d’une valeur secrète, la lecture retourne le hash tel quel.
php artisan make:cast AsHash --inbound
<?phpnamespace App\Casts;use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;use Illuminate\Database\Eloquent\Model;class AsHash implements CastsInboundAttributes{ public function __construct( protected string|null $algorithm = null, ) {} /** * Hache lors de la sauvegarde */ public function set( Model $model, string $key, mixed $value, array $attributes, ): string { return is_null($this->algorithm) ? bcrypt($value) : hash($this->algorithm, $value); }}
Pour passer des paramètres à un cast personnalisé, spécifiez-les après le nom de la classe séparés par un :. Plusieurs paramètres sont séparés par des virgules.
Castables : porter la logique de cast dans le Value Object
Un Value Object implémentant l’interface Castable a une méthode castUsing qui retourne sa propre classe de cast. Comme le modèle n’a plus besoin de connaître la classe de cast, la logique métier gagne en clarté.
<?phpnamespace App\ValueObjects;use App\Casts\AsAddress;use Illuminate\Contracts\Database\Eloquent\Castable;class Address implements Castable{ /** * Retourne la classe utilisée pour le cast de cet objet * * @param array<string, mixed> $arguments */ public static function castUsing(array $arguments): string { return AsAddress::class; }}
Côté modèle, spécifiez la classe Value Object au lieu de la classe de cast.
protected function casts(): array{ return [ 'address' => Address::class, ];}
En combinant Castable avec une classe anonyme, vous pouvez regrouper le Value Object et la logique de cast dans un même fichier.
class Address implements Castable{ public static function castUsing(array $arguments): CastsAttributes { return new class implements CastsAttributes { public function get(Model $model, string $key, mixed $value, array $attributes): Address { return new Address( $attributes['address_line_one'], $attributes['address_line_two'], ); } public function set(Model $model, string $key, mixed $value, array $attributes): array { return [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ]; } }; }}
Les casts, $appends et $hidden sont des mécanismes indépendants, mais leur combinaison demande de l’attention.
class User extends Model{ protected function casts(): array { return [ 'address' => AsAddress::class, ]; } // Exclure address_line_one/two du toArray() / toJson() protected $hidden = ['address_line_one', 'address_line_two']; // Inclure l'address casté dans la sérialisation protected $appends = ['address'];}
Ce qui est spécifié dans $hidden correspond aux noms de colonnes BDD. Précisez les noms de colonnes originales (address_line_one, address_line_two) plutôt que le nom d’attribut produit par le cast (address).