Files
alfa-bank-sdk/_docs/laravel-libs/laravel-data/as-a-data-transfer-object/model-to-data-object.md

262 lines
5.4 KiB
Markdown

---
title: From a model
weight: 11
---
It is possible to create a data object from a model, let's say we have the following model:
```php
class Artist extends Model
{
}
```
It has the following columns in the database:
- id
- first_name
- last_name
- created_at
- updated_at
We can create a data object from this model like this:
```php
class ArtistData extends Data
{
public int $id;
public string $first_name;
public string $last_name;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
We now can create a data object from the model like this:
```php
$artist = ArtistData::from(Artist::find(1));
```
## Casts
A model can have casts, these casts will be called before a data object is created. Let's extend the model:
```php
class Artist extends Model
{
public function casts(): array
{
return [
'properties' => 'array'
];
}
}
```
Within the database the new column will be stored as a JSON string, but in the data object we can just use the array
type:
```php
class ArtistData extends Data
{
public int $id;
public string $first_name;
public string $last_name;
public array $properties;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
## Attributes & Accessors
Laravel allows you to define attributes on a model, these will be called before a data object is created. Let's extend
the model:
```php
class Artist extends Model
{
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => $this->first_name . ' ' . $this->last_name,
);
}
}
```
We now can use the attribute in the data object:
```php
class ArtistData extends Data
{
public int $id;
public string $full_name;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
Remember: we need to use the snake_case version of the attribute in the data object since that's how it is stored in the
model. Read on for a more elegant solution when you want to use camelCase property names in your data object.
It is also possible to define accessors on a model which are the successor of the attributes:
```php
class Artist extends Model
{
public function getFullName(): Attribute
{
return Attribute::get(fn () => "{$this->first_name} {$this->last_name}");
}
}
```
With the same data object we created earlier we can now use the accessor.
## Mapping property names
Sometimes you want to use camelCase property names in your data object, but the model uses snake_case. You can use
an `MapInputName` to map the property names:
```php
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
class ArtistData extends Data
{
public int $id;
#[MapInputName(SnakeCaseMapper::class)]
public string $fullName;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
An even more elegant solution would be to map every property within the data object:
```php
#[MapInputName(SnakeCaseMapper::class)]
class ArtistData extends Data
{
public int $id;
public string $fullName;
public CarbonImmutable $createdAt;
public CarbonImmutable $updatedAt;
}
```
## Relations
Let's create a new model:
```php
class Song extends Model
{
public function artist(): BelongsTo
{
return $this->belongsTo(Artist::class);
}
}
```
Which has the following columns in the database:
- id
- artist_id
- title
We update our previous model as such:
```php
class Artist extends Model
{
public function songs(): HasMany
{
return $this->hasMany(Song::class);
}
}
```
We can now create a data object like this:
```php
class SongData extends Data
{
public int $id;
public string $title;
}
```
And update our previous data object like this:
```php
class ArtistData extends Data
{
public int $id;
/** @var array<SongData> */
public array $songs;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
We can now create a data object with the relations like this:
```php
$artist = ArtistData::from(Artist::with('songs')->find(1));
```
When you're not loading the relations in advance, `null` will be returned for the relation.
It is however possible to load the relation on the fly by adding the `LoadRelation` attribute to the property:
```php
class ArtistData extends Data
{
public int $id;
/** @var array<SongData> */
#[LoadRelation]
public array $songs;
public CarbonImmutable $created_at;
public CarbonImmutable $updated_at;
}
```
Now the data object with relations can be created like this:
```php
$artist = ArtistData::from(Artist::find(1));
```
We even eager-load the relation for performance, neat!
### Be careful with automatic loading of relations
Let's update the `SongData` class like this:
```php
class SongData extends Data
{
public int $id;
public string $title;
#[LoadRelation]
public ArtistData $artist;
}
```
When we now create a data object like this:
```php
$song = SongData::from(Song::find(1));
```
We'll end up in an infinite loop, since the `SongData` class will try to load the `ArtistData` class, which will try to
load the `SongData` class, and so on.
## Missing attributes
When a model is missing attributes and `preventAccessingMissingAttributes` is enabled for a model the `MissingAttributeException` won't be thrown when creating a data object with a property that can be null or Optional.