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

# Eigene Casts in Eloquent

> Wie Sie eigene Casts durch Implementierung des Interfaces CastsAttributes erstellen. Fortgeschrittene Muster wie Value Objects, Inbound-Casts und Castables werden ebenfalls vorgestellt.

## Was sind Casts?

Eloquent-Casts wandeln aus der Datenbank gelesene Rohwerte in PHP-Typen um und beim Speichern in die umgekehrte Richtung. Sie werden über die Methode `casts` definiert.

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

## Arten der integrierten Casts

Liste der von Laravel mitgelieferten Casts.

| Cast                   | Beschreibung                                               |
| ---------------------- | ---------------------------------------------------------- |
| `integer` / `int`      | In Integer umwandeln                                       |
| `float` / `double`     | In Fließkommazahl umwandeln                                |
| `string`               | In String umwandeln                                        |
| `boolean` / `bool`     | In Boolean umwandeln (inklusive `0`/`1`)                   |
| `array`                | JSON-String ↔ PHP-Array                                    |
| `collection`           | JSON-String ↔ Collection-Instanz                           |
| `object`               | JSON-String ↔ stdObject-Instanz                            |
| `datetime`             | String ↔ Carbon-Instanz                                    |
| `immutable_datetime`   | String ↔ CarbonImmutable-Instanz                           |
| `date`                 | String ↔ Carbon (ohne Zeit)                                |
| `timestamp`            | String ↔ UNIX-Zeitstempel                                  |
| `encrypted`            | Verschlüsselung beim Speichern, Entschlüsselung beim Lesen |
| `hashed`               | Beim Speichern hashen (kombiniert mit Read-only-Casts)     |
| `AsStringable::class`  | String ↔ Stringable-Objekt                                 |
| `AsArrayObject::class` | JSON ↔ ArrayObject-Instanz                                 |
| `AsCollection::class`  | JSON ↔ Collection-Instanz                                  |

<Info>
  `AsArrayObject` und `AsCollection` sind Laravel-intern als eigene Casts implementiert, damit einzelne Offsets eines Arrays direkt geändert werden können.
</Info>

## Eine eigene Cast-Klasse erstellen

Wenn die eingebauten Casts nicht ausreichen, erstellen Sie eine eigene Cast-Klasse, die das Interface `CastsAttributes` implementiert.

### Definition des Interfaces

Der Framework-Contract sieht folgendermaßen aus:

```php theme={null}
// src/Illuminate/Contracts/Database/Eloquent/CastsAttributes.php

interface CastsAttributes
{
    /**
     * Wandelt den DB-Rohwert in einen PHP-Wert (beim Lesen).
     *
     * @param  array<string, mixed>  $attributes  Alle Attributwerte des Modells
     */
    public function get(Model $model, string $key, mixed $value, array $attributes);

    /**
     * Wandelt einen PHP-Wert in ein DB-taugliches Format (beim Schreiben).
     *
     * @param  array<string, mixed>  $attributes  Alle Attributwerte des Modells
     */
    public function set(Model $model, string $key, mixed $value, array $attributes);
}
```

Weil das Argument `$attributes` alle Modellattribute enthält, sind auch spaltenübergreifende Umwandlungen möglich (siehe Value-Object-Muster unten).

### Grundlegende Implementierung eines eigenen Casts

Erzeugen Sie ein Gerüst mit `make:cast`.

```bash theme={null}
php artisan make:cast AsMoney
```

Die Datei `app/Casts/AsMoney.php` wird angelegt. Als Beispiel implementieren wir einen Cast, der einen als Integer gespeicherten Geldbetrag in ein `Money`-Value-Object umwandelt.

```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
{
    /**
     * Wandelt den DB-Integerwert (in Yen) in ein Money-Objekt um.
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Money {
        return new Money((int) $value);
    }

    /**
     * Wandelt ein Money-Objekt in einen DB-tauglichen Integer um.
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): int {
        if ($value instanceof Money) {
            return $value->amount;
        }

        return (int) $value;
    }
}
```

Aktivieren Sie den Cast am Modell:

```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,
        ];
    }
}
```

`$order->price` liefert nun eine `Money`-Instanz.

## Value-Object-Casts

Ein Muster, um mehrere DB-Spalten als ein einziges Value Object zu behandeln.

### Beispiel: Adress-Cast

Die zwei Spalten `address_line_one` und `address_line_two` werden in ein `Address`-Value-Object zusammengefasst.

```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
{
    /**
     * Aus mehreren Spalten ein Address-Objekt zusammenbauen.
     */
    public function get(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): Address {
        return new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        );
    }

    /**
     * Ein Address-Objekt in ein Array pro Spalte zerlegen.
     *
     * @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>
  Gibt `set` ein Array zurück, verwendet Eloquent die Schlüssel als Spaltennamen und speichert die Werte in die jeweiligen Spalten. Bei Ein-Spalten-Casts geben Sie einen String oder Integer zurück.
</Info>

Die Anwendung am Modell und die Nutzung:

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

// Als Address-Objekt lesen
echo $user->address->lineOne;

// Änderungen am Value Object werden beim Speichern automatisch in die DB übertragen
$user->address = new Address('123 Main St', 'Apt 4B');
$user->save();
```

### Value-Object-Cache

In Value Objects umgewandelte Attributwerte werden von Eloquent gecacht. Beim zweifachen Zugriff auf dasselbe Attribut erhalten Sie dieselbe Instanz.

Wenn Sie den Cache deaktivieren möchten, fügen Sie in der Cast-Klasse die Property `$withoutObjectCaching` hinzu.

```php theme={null}
class AsAddress implements CastsAttributes
{
    public bool $withoutObjectCaching = true;

    // ...
}
```

## Inbound-Casts (nur Schreiben)

Ein Cast, der nur beim Schreiben in die DB umwandelt, beim Lesen aber nicht. Implementiert das Interface `CastsInboundAttributes`.

Typischer Anwendungsfall ist Hashing: Beim Speichern von Passwörtern oder geheimen Werten wird gehasht, beim Lesen wird der Hash unverändert zurückgegeben.

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

    /**
     * Beim Speichern hashen.
     */
    public function set(
        Model $model,
        string $key,
        mixed $value,
        array $attributes,
    ): string {
        return is_null($this->algorithm)
            ? bcrypt($value)
            : hash($this->algorithm, $value);
    }
}
```

## Cast-Parameter

Parameter an einen Cast übergeben Sie nach dem Klassennamen mit Doppelpunkt getrennt. Mehrere Parameter trennen Sie durch Komma.

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

Die Parameter werden dem Konstruktor der Cast-Klasse übergeben.

```php theme={null}
class AsHash implements CastsInboundAttributes
{
    public function __construct(
        protected string|null $algorithm = null, // ':sha256' wird übergeben
    ) {}
}
```

## Castables: Cast-Logik im Value Object

Ein Value Object, das das Interface `Castable` implementiert, besitzt eine Methode `castUsing`, die die zu verwendende Cast-Klasse zurückgibt. So muss das Modell die Cast-Klasse nicht kennen und die Domänenlogik bleibt aufgeräumt.

```php theme={null}
<?php

namespace App\ValueObjects;

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

class Address implements Castable
{
    /**
     * Gibt die zu verwendende Cast-Klasse zurück.
     *
     * @param  array<string, mixed>  $arguments
     */
    public static function castUsing(array $arguments): string
    {
        return AsAddress::class;
    }
}
```

Am Modell geben Sie statt der Cast-Klasse die Value-Object-Klasse an.

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

<Tip>
  In Kombination mit `Castable` und einer anonymen Klasse können Sie Value Object und Cast-Logik in einer Datei bündeln.

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

## Zusammenspiel mit `$appends` und `$hidden`

Casts, `$appends` und `$hidden` sind eigenständige Mechanismen. Bei der Kombination ist Sorgfalt geboten.

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

    // address bei toArray()/toJson() ausschließen
    protected $hidden = ['address_line_one', 'address_line_two'];

    // Gecastetes address im Serialisierungsergebnis mitliefern
    protected $appends = ['address'];
}
```

<Warning>
  In `$hidden` geben Sie DB-Spaltennamen an. Nicht den durch den Cast entstandenen Attributnamen (`address`), sondern die Ursprungsnamen (`address_line_one`, `address_line_two`).
</Warning>

## Casts zur Laufzeit ergänzen

Wenn Sie nur für eine bestimmte Query oder Anfrage Casts ergänzen wollen, nutzen Sie `mergeCasts`.

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

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

## Nächste Schritte

<Card title="Eloquent-Observer und Modell-Events" icon="bell" href="/de/advanced/eloquent-observers">
  Lernen Sie, wie Sie sich in Lebenszyklus-Events wie Speichern oder Löschen einklinken und eigene Verarbeitung ergänzen.
</Card>


## Related topics

- [Eloquent: Accessors, Mutators, Casts](/de/eloquent-mutators.md)
- [Upgrade von Laravel 10 auf 11](/de/blog/upgrade-10-to-11.md)
- [Eloquent-Scopes](/de/advanced/eloquent-scopes.md)
- [Eloquent-API-Ressourcen](/de/eloquent-resources.md)
- [Eigene Auth-Guards implementieren](/de/advanced/custom-auth-guard.md)
