Eloquent casting | laravel-data | Spatie

 SPATIE

  Laravel Data
===============

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-data](https://spatie.be/docs/laravel-data/v3)  Advanced-usage  Eloquent casting

 Version   v4   v3   v2   v1

 Other versions for crawler [v4](https://spatie.be/docs/laravel-data/v4) [v3](https://spatie.be/docs/laravel-data/v3) [v2](https://spatie.be/docs/laravel-data/v2) [v1](https://spatie.be/docs/laravel-data/v1)

- [ Introduction ](https://spatie.be/docs/laravel-data/v3/introduction)
- [ Support us ](https://spatie.be/docs/laravel-data/v3/support-us)
- [ Requirements ](https://spatie.be/docs/laravel-data/v3/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-data/v3/installation-setup)
- [ Third party packages ](https://spatie.be/docs/laravel-data/v3/third-party-packages)
- [ Questions and issues ](https://spatie.be/docs/laravel-data/v3/questions-issues)
- [ Changelog ](https://spatie.be/docs/laravel-data/v3/changelog)
- [ About us ](https://spatie.be/docs/laravel-data/v3/about-us)

Getting started
---------------

- [ Quickstart ](https://spatie.be/docs/laravel-data/v3/getting-started/quickstart)

As a DTO
--------

- [ Creating a data object ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/creating-a-data-object)
- [ Optional properties ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/optional-properties)
- [ Nesting ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/nesting)
- [ Collections ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/collections)
- [ Casts ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/casts)
- [ Default values ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/defaults)
- [ Computed values ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/computed)
- [ From a request ](https://spatie.be/docs/laravel-data/v3/as-a-data-transfer-object/request-to-data-object)

As a resource
-------------

- [ From data to resource ](https://spatie.be/docs/laravel-data/v3/as-a-resource/from-data-to-resource)
- [ Including and excluding properties ](https://spatie.be/docs/laravel-data/v3/as-a-resource/lazy-properties)
- [ Wrapping ](https://spatie.be/docs/laravel-data/v3/as-a-resource/wrapping)
- [ Transforming data ](https://spatie.be/docs/laravel-data/v3/as-a-resource/transformers)

Advanced usage
--------------

- [ Eloquent casting ](https://spatie.be/docs/laravel-data/v3/advanced-usage/eloquent-casting)
- [ Transforming to TypeScript ](https://spatie.be/docs/laravel-data/v3/advanced-usage/typescript)
- [ Working with dates ](https://spatie.be/docs/laravel-data/v3/advanced-usage/working-with-dates)
- [ Normalizers ](https://spatie.be/docs/laravel-data/v3/advanced-usage/normalizers)
- [ Pipeline ](https://spatie.be/docs/laravel-data/v3/advanced-usage/pipeline)
- [ Use with Inertia ](https://spatie.be/docs/laravel-data/v3/advanced-usage/use-with-inertia)
- [ Use with Livewire ](https://spatie.be/docs/laravel-data/v3/advanced-usage/use-with-livewire)
- [ Creating a cast ](https://spatie.be/docs/laravel-data/v3/advanced-usage/creating-a-cast)
- [ Creating a transformer ](https://spatie.be/docs/laravel-data/v3/advanced-usage/creating-a-transformer)
- [ Filling properties from route parameters ](https://spatie.be/docs/laravel-data/v3/advanced-usage/filling%20from-route-parameters)
- [ Creating a rule inferrer ](https://spatie.be/docs/laravel-data/v3/advanced-usage/creating-a-rule-inferrer)
- [ Internal structures ](https://spatie.be/docs/laravel-data/v3/advanced-usage/internal-structures)
- [ Custom collections ](https://spatie.be/docs/laravel-data/v3/advanced-usage/custom-collections)
- [ Mapping rules ](https://spatie.be/docs/laravel-data/v3/advanced-usage/mapping-rules)
- [ Validation attributes ](https://spatie.be/docs/laravel-data/v3/advanced-usage/validation-attributes)
- [ Performance ](https://spatie.be/docs/laravel-data/v3/advanced-usage/performance)
- [ Commands ](https://spatie.be/docs/laravel-data/v3/advanced-usage/commands)

      You are viewing the documentation for **an older version** of this package. You can check the version you are using with the following command:

 `                                    composer show spatie/laravel-data                                                                                                                                                                                                                                    `

Eloquent casting
================

###  On this page

1. [ Casting data collections ](#content-casting-data-collections)
2. [ Using defaults for null database values ](#content-using-defaults-for-null-database-values)

Since data objects can be created from arrays and be easily transformed into arrays back again, they are excellent to be used with [Eloquent casts](https://laravel.com/docs/8.x/eloquent-mutators#custom-casts):

```
class Song extends Model
{
    protected $casts = [
        'artist' => ArtistData::class,
    ];
}
```

Now you can store a data object in a model as such:

```
Song::create([
    'artist' => new ArtistData(name: 'Rick Astley', age: 22),
]);
```

It is also possible to use an array representation of the data object:

```
Song::create([
    'artist' => [
        'name' => 'Rick Astley',
        'age' => 22
    ]
]);
```

This will internally be converted to a data object which you can later retrieve as such:

```
Song::findOrFail($id)->artist; // ArtistData object
```

### Abstract data objects

Sometimes you have an abstract parent data object with multiple child data objects, for example:

```
abstract class RecordConfig extends Data
{
    public function __construct(
        public int $tracks,
    ) {}
}

class CdRecordConfig extends RecordConfig
{
    public function __construct(
        int $tracks
        public int $bytes,
    ) {
        parent::__construct($tracks);
    }
}

class VinylRecordConfig extends RecordConfig
{
    public function __construct(
        int $tracks
        public int $rpm,
    ) {
        parent::__construct($tracks);
    }
}
```

A model can have a JSON field which is either one of these data objects:

```
class Record extends Model
{
    protected $casts = [
        'config' => RecordConfig::class,
    ];
}
```

You can then store either a `CdRecordConfig` or a `VinylRecord` in the `config` field:

```
$cdRecord = Record::create([
    'config' => new CdRecordConfig(tracks: 12, bytes: 1000),
]);

$vinylRecord = Record::create([
    'config' => new VinylRecordConfig(tracks: 12, rpm: 33),
]);

$cdRecord->config; // CdRecordConfig object
$vinylRecord->config; // VinylRecordConfig object
```

When a data object class is abstract and used as an Eloquent cast then this feature will work out of the box.

The child data object value of the model will be stored in the database as a JSON string with the class name as the key:

```
{
    "type": "\\App\\Data\\CdRecordConfig",
    "value": {
        "tracks": 12,
        "bytes": 1000
    }
}
```

When retrieving the model, the data object will be instantiated based on the `type` key in the JSON string.

#### Abstract data class morphs

By default, the `type` key in the JSON string will be the fully qualified class name of the child data object. This can break your application quite easily when you refactor your code. To prevent this, you can add a morph map like with [Eloquent models](https://laravel.com/docs/10.x/eloquent-relationships#polymorphic-relationships). Within your `AppServiceProvivder` you can add the following mapping:

```
use Spatie\LaravelData\Support\DataConfig;

app(DataConfig::class)->enforceMorphMap([
    'cd_record_config' => CdRecordConfig::class,
    'vinyl_record_config' => VinylRecordConfig::class,
]);
```

Casting data collections
--------------------------------------------------------------------------------------------------------------------------------

It is also possible to store data collections in an Eloquent model:

```
class Artist extends Model
{
    protected $casts = [
        'songs' => DataCollection::class.':'.SongData::class,
    ];
}
```

A collection of data objects within the Eloquent model can be made as such:

```
Artist::create([
    'songs' => [
        new SongData(title: 'Never gonna give you up', artist: 'Rick Astley'),
        new SongData(title: 'Together Forever', artist: 'Rick Astley'),
    ],
]);
```

It is also possible to provide an array instead of a data object to the collection:

```
Artist::create([
    'songs' => [
        ['title' => 'Never gonna give you up', 'artist' => 'Rick Astley'],
        ['title' => 'Together Forever', 'artist' => 'Rick Astley']
    ],
]);
```

Using defaults for null database values
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

By default, if a database value is `null`, then the model attribute will also be `null`. However, sometimes you might want to instantiate the attribute with some default values.

To achieve this, you may provide an additional `default` [Cast Parameter](https://laravel.com/docs/eloquent-mutators#cast-parameters) to ensure the caster gets instantiated.

```
class Song extends Model
{
    protected $casts = [
        'artist' => ArtistData::class . ':default',
    ];
}
```

This will ensure that the `ArtistData` caster is instantiated even when the `artist` attribute in the database is `null`.

You may then specify some default values in the cast which will be used instead.

```
class ArtistData extends Data
{
    public string $name = 'Default name';
}
```

```
Song::findOrFail($id)->artist->name; // 'Default name'
```

### Nullable collections

You can also use the `default` argument in the case where you *always* want a `DataCollection` to be returned.

The first argument (after `:`) should always be the data class to be used with the `DataCollection`, but you can add `default` as a comma separated second argument.

```
class Artist extends Model
{
    protected $casts = [
        'songs' => DataCollection::class.':'.SongData::class.',default',
    ];
}
```

```
$artist = Artist::create([
    'songs' => null
]);

$artist->songs; // DataCollection
$artist->songs->count();// 0
```
