Vai al contenuto principale

Cosa sono i cast

I cast di Eloquent sono il meccanismo che converte il valore grezzo letto dal database in un tipo PHP e, al salvataggio, esegue la conversione inversa. Si definiscono con il metodo casts.
protected function casts(): array
{
    return [
        'is_admin'   => 'boolean',
        'settings'   => 'array',
        'created_at' => 'datetime',
    ];
}

Cast integrati

Elenco dei cast forniti di default da Laravel.
CastDescrizione
integer / intConverte in intero
float / doubleConverte in floating point
stringConverte in stringa
boolean / boolConverte in booleano (include 0/1)
arrayStringa JSON ↔ array PHP
collectionStringa JSON ↔ istanza Collection
objectStringa JSON ↔ istanza stdObject
datetimeStringa ↔ istanza Carbon
immutable_datetimeStringa ↔ istanza CarbonImmutable
dateStringa ↔ Carbon (senza orario)
timestampStringa ↔ timestamp UNIX
encryptedCifra al salvataggio, decifra alla lettura
hashedFa l’hash al salvataggio (da combinare con un cast in sola lettura)
AsStringable::classStringa ↔ oggetto Stringable
AsArrayObject::classJSON ↔ istanza ArrayObject
AsCollection::classJSON ↔ istanza Collection
AsArrayObject e AsCollection sono implementati internamente in Laravel come cast personalizzati per permettere la modifica diretta di specifici offset dell’array.

Creare una classe di cast personalizzato

Se i cast integrati non bastano, crei un cast personalizzato che implementa l’interfaccia CastsAttributes.

Definizione dell’interfaccia

Il contract nel framework è definito così.
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.php

interface CastsAttributes
{
    /**
     * Converte il valore grezzo dal DB in un valore PHP (lettura)
     *
     * @param  array<string, mixed>  $attributes  tutti gli attributi del modello
     */
    public function get(Model $model, string $key, mixed $value, array $attributes);

    /**
     * Converte un valore PHP in una forma salvabile nel DB (scrittura)
     *
     * @param  array<string, mixed>  $attributes  tutti gli attributi del modello
     */
    public function set(Model $model, string $key, mixed $value, array $attributes);
}
L’argomento $attributes contiene tutti gli attributi del modello, quindi puoi eseguire conversioni che coinvolgono più colonne (vedi il pattern Value Object più avanti).

Implementare un cast personalizzato di base

Il comando make:cast genera lo scaffold.
php artisan make:cast AsMoney
Viene generato app/Casts/AsMoney.php. Come esempio implementiamo un cast che converte un importo (salvato come intero) in un Value Object Money.
<?php

namespace App\Casts;

use App\ValueObjects\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class AsMoney implements CastsAttributes
{
    /**
     * Converte l'intero del DB (in centesimi) in un oggetto Money
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Money {
        return new Money((int) $value);
    }

    /**
     * Converte un oggetto Money in un intero salvabile nel DB
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): int {
        if ($value instanceof Money) {
            return $value->amount;
        }

        return (int) $value;
    }
}
Applica il cast al modello.
<?php

namespace App\Models;

use App\Casts\AsMoney;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected function casts(): array
    {
        return [
            'price' => AsMoney::class,
        ];
    }
}
Ora $order->price restituisce un’istanza Money.

Cast Value Object

Il pattern in cui più colonne del DB vengono trattate come un unico Value Object.

Esempio: cast di un indirizzo

Aggreghiamo address_line_one e address_line_two in un Value Object Address.
<?php

namespace 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();
    }
}
<?php

namespace App\Casts;

use App\ValueObjects\Address;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;

class AsAddress implements CastsAttributes
{
    /**
     * Costruisce l'oggetto Address da più colonne
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Address {
        return new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        );
    }

    /**
     * Scompone l'oggetto Address in un array indicizzato per colonna
     *
     * @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,
        ];
    }
}
Se set restituisce un array, Eloquent usa le chiavi come nomi di colonna e salva ciascun valore nella colonna corrispondente. Nei cast su una sola colonna restituisci una stringa o un intero.
Applicazione al modello e utilizzo:
<?php

namespace 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);

// puoi accedere come oggetto Address
echo $user->address->lineOne;

// modificando il Value Object, il salvataggio si riflette sul DB automaticamente
$user->address = new Address('123 Main St', 'Apt 4B');
$user->save();

Cache dei Value Object

Gli attributi convertiti in Value Object vengono messi in cache da Eloquent. Accedendo due volte allo stesso attributo, ottieni la stessa istanza. Per disabilitare la cache aggiungi la proprietà $withoutObjectCaching alla classe cast.
class AsAddress implements CastsAttributes
{
    public bool $withoutObjectCaching = true;

    // ...
}

Cast inbound (solo scrittura)

Cast che convertono solo in scrittura verso il DB e non in lettura. Implementano l’interfaccia CastsInboundAttributes. L’uso tipico è l’hashing. Quando salvi password o segreti, converti; in lettura restituisci direttamente l’hash.
php artisan make:cast AsHash --inbound
<?php

namespace 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,
    ) {}

    /**
     * Applica l'hash al salvataggio
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): string {
        return is_null($this->algorithm)
            ? bcrypt($value)
            : hash($this->algorithm, $value);
    }
}

Parametri del cast

Per passare parametri a un cast personalizzato, indicali dopo il nome della classe separati da :. Più parametri si separano con ,.
protected function casts(): array
{
    return [
        'secret' => AsHash::class.':sha256',
        'data'   => AsHash::class.':sha512',
    ];
}
I parametri vengono passati al costruttore della classe cast.
class AsHash implements CastsInboundAttributes
{
    public function __construct(
        protected string|null $algorithm = null, // riceve ':sha256'
    ) {}
}

Castables: portare la logica di cast sul Value Object

Un Value Object che implementa l’interfaccia Castable ha il metodo castUsing che restituisce la classe di cast. Il modello non deve conoscere la classe cast, e la logica di dominio resta ordinata.
<?php

namespace App\ValueObjects;

use App\Casts\AsAddress;
use Illuminate\Contracts\Database\Eloquent\Castable;

class Address implements Castable
{
    /**
     * Restituisce la classe da usare per il cast di questo oggetto
     *
     * @param  array<string, mixed>  $arguments
     */
    public static function castUsing(array $arguments): string
    {
        return AsAddress::class;
    }
}
Nel modello indichi la classe Value Object al posto della classe cast.
protected function casts(): array
{
    return [
        'address' => Address::class,
    ];
}
Combinando Castable con una classe anonima, puoi mantenere Value Object e logica di cast in un unico file.
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,
                ];
            }
        };
    }
}

Interazione con $appends e $hidden

Cast, $appends e $hidden sono meccanismi indipendenti, ma quando li combini serve attenzione.
class User extends Model
{
    protected function casts(): array
    {
        return [
            'address' => AsAddress::class,
        ];
    }

    // esclude address in toArray() / toJson()
    protected $hidden = ['address_line_one', 'address_line_two'];

    // include l'address castato nel risultato serializzato
    protected $appends = ['address'];
}
In $hidden indica i nomi di colonna del DB. Non l’attributo prodotto dal cast (address), ma i nomi originali delle colonne (address_line_one, address_line_two).

Aggiungere cast a runtime

Se vuoi aggiungere un cast solo per una specifica query o richiesta usa il metodo mergeCasts.
$user = User::find(1);

$user->mergeCasts([
    'extra_data' => 'array',
]);

Prossimi passi

Eloquent Observer ed eventi del modello

Impara come agganciarti agli eventi del ciclo di vita del modello (salvataggio, cancellazione, ecc.) per aggiungere logica prima e dopo.
Ultima modifica il 13 luglio 2026