> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Cast personalizzati di Eloquent

> Come creare cast personalizzati implementando l'interfaccia CastsAttributes. Presenta anche pattern avanzati come Value Object, cast inbound e Castables.

## 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`.

```php theme={null}
protected function casts(): array
{
    return [
        'is_admin'   => 'boolean',
        'settings'   => 'array',
        'created_at' => 'datetime',
    ];
}
```

## Cast integrati

Elenco dei cast forniti di default da Laravel.

| Cast                   | Descrizione                                                         |
| ---------------------- | ------------------------------------------------------------------- |
| `integer` / `int`      | Converte in intero                                                  |
| `float` / `double`     | Converte in floating point                                          |
| `string`               | Converte in stringa                                                 |
| `boolean` / `bool`     | Converte in booleano (include `0`/`1`)                              |
| `array`                | Stringa JSON ↔ array PHP                                            |
| `collection`           | Stringa JSON ↔ istanza Collection                                   |
| `object`               | Stringa JSON ↔ istanza stdObject                                    |
| `datetime`             | Stringa ↔ istanza Carbon                                            |
| `immutable_datetime`   | Stringa ↔ istanza CarbonImmutable                                   |
| `date`                 | Stringa ↔ Carbon (senza orario)                                     |
| `timestamp`            | Stringa ↔ timestamp UNIX                                            |
| `encrypted`            | Cifra al salvataggio, decifra alla lettura                          |
| `hashed`               | Fa l'hash al salvataggio (da combinare con un cast in sola lettura) |
| `AsStringable::class`  | Stringa ↔ oggetto Stringable                                        |
| `AsArrayObject::class` | JSON ↔ istanza ArrayObject                                          |
| `AsCollection::class`  | JSON ↔ istanza Collection                                           |

<Info>
  `AsArrayObject` e `AsCollection` sono implementati internamente in Laravel come cast personalizzati per permettere la modifica diretta di specifici offset dell'array.
</Info>

## 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ì.

```php theme={null}
// 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.

```bash theme={null}
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 theme={null}
<?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 theme={null}
<?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 theme={null}
<?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 theme={null}
<?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,
        ];
    }
}
```

<Info>
  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.
</Info>

Applicazione al modello e utilizzo:

```php theme={null}
<?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,
        ];
    }
}
```

```php theme={null}
$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.

```php theme={null}
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.

```bash theme={null}
php artisan make:cast AsHash --inbound
```

```php theme={null}
<?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 `,`.

```php theme={null}
protected function casts(): array
{
    return [
        'secret' => AsHash::class.':sha256',
        'data'   => AsHash::class.':sha512',
    ];
}
```

I parametri vengono passati al costruttore della classe cast.

```php theme={null}
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 theme={null}
<?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.

```php theme={null}
protected function casts(): array
{
    return [
        'address' => Address::class,
    ];
}
```

<Tip>
  Combinando `Castable` con una classe anonima, puoi mantenere Value Object e logica di cast in un unico file.

  ```php theme={null}
  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,
                  ];
              }
          };
      }
  }
  ```
</Tip>

## Interazione con `$appends` e `$hidden`

Cast, `$appends` e `$hidden` sono meccanismi indipendenti, ma quando li combini serve attenzione.

```php theme={null}
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'];
}
```

<Warning>
  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`).
</Warning>

## Aggiungere cast a runtime

Se vuoi aggiungere un cast solo per una specifica query o richiesta usa il metodo `mergeCasts`.

```php theme={null}
$user = User::find(1);

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

## Prossimi passi

<Card title="Eloquent Observer ed eventi del modello" icon="bell" href="/it/advanced/eloquent-observers">
  Impara come agganciarti agli eventi del ciclo di vita del modello (salvataggio, cancellazione, ecc.) per aggiungere logica prima e dopo.
</Card>


## Related topics

- [Scope di Eloquent](/it/advanced/eloquent-scopes.md)
- [Accessor, mutator e cast di Eloquent](/it/eloquent-mutators.md)
- [Aggiornamento da Laravel 8 a 9](/it/blog/upgrade-8-to-9.md)
- [Agenti personalizzati](/it/packages/laravel-copilot-sdk/custom-agents.md)
- [Provider personalizzati](/it/packages/laravel-copilot-sdk/custom-providers.md)
