Идёт обновление сайта. Несколько дней возможны сбои в оформлении и переводах. Документация работает — если страница выглядит сломанной, обновите её позже.

Документация
L Laravel L intervention/image
Войти
Главная Laravel 10.x Eloquent: Мутаторы и приведение типов

Eloquent: Мутаторы и приведение типов

10.x 7 мар 2026 г.

#Введение

Аксессоры, мутаторы и кастинг атрибутов позволяют преобразовывать значения атрибутов Eloquent при их получении или установке в экземплярах моделей. Например, вы можете использовать Laravel encrypter для шифрования значения при сохранении в базе данных, а затем автоматически расшифровывать атрибут при доступе к нему через модель Eloquent. Или вы можете преобразовать JSON-строку, хранящуюся в базе данных, в массив при доступе к ней через модель Eloquent.

#Аксессоры и мутаторы

#Определение аксессора

Аксессор преобразует значение атрибута Eloquent при его получении. Чтобы определить аксессор, создайте защищённый метод в вашей модели, который будет представлять доступный атрибут. Имя метода должно соответствовать "camel case" представлению реального атрибута модели / столбца базы данных, если это применимо.

В этом примере мы определим аксессор для атрибута first_name. Аксессор будет автоматически вызван Eloquent при попытке получить значение атрибута first_name. Все методы аксессоров и мутаторов должны объявлять возвращаемый тип Illuminate\Database\Eloquent\Casts\Attribute:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Получить имя пользователя.
     */
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
        );
    }
}

Все методы аксессоров возвращают экземпляр Attribute, который определяет, как атрибут будет получен и, опционально, изменён. В этом примере мы определяем только способ получения атрибута. Для этого мы передаём аргумент get в конструктор класса Attribute.

Как видите, исходное значение столбца передаётся в аксессор, что позволяет вам обработать и вернуть значение. Чтобы получить значение аксессора, достаточно обратиться к атрибуту first_name у экземпляра модели:

use App\Models\User;

$user = User::find(1);

$firstName = $user->first_name;
Примечание

Если вы хотите, чтобы эти вычисляемые значения добавлялись в массивное или JSON-представление вашей модели, вам нужно будет добавить их в список добавляемых атрибутов.

#Создание объектов-значений из нескольких атрибутов

Иногда аксессор должен преобразовывать несколько атрибутов модели в один объект-значение. Для этого ваш замыкание get может принимать второй аргумент $attributes, который автоматически передаётся и содержит массив всех текущих атрибутов модели:

use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;

/**
 * Взаимодействие с адресом пользователя.
 */
protected function address(): Attribute
{
    return Attribute::make(
        get: fn (mixed $value, array $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
    );
}

#Кэширование аксессоров

При возврате объектов-значений из аксессоров любые изменения, внесённые в объект-значение, автоматически синхронизируются с моделью перед её сохранением. Это возможно, потому что Eloquent сохраняет экземпляры, возвращаемые аксессорами, чтобы возвращать один и тот же экземпляр при каждом вызове аксессора:

use App\Models\User;

$user = User::find(1);

$user->address->lineOne = 'Обновлённое значение адреса, строка 1';
$user->address->lineTwo = 'Обновлённое значение адреса, строка 2';

$user->save();

Однако иногда вы можете захотеть включить кэширование для примитивных значений, таких как строки и булевы значения, особенно если их вычисление ресурсоёмко. Для этого можно вызвать метод shouldCache при определении аксессора:

protected function hash(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => bcrypt(gzuncompress($value)),
    )->shouldCache();
}

Если вы хотите отключить поведение кэширования объектов для атрибутов, можно вызвать метод withoutObjectCaching при определении атрибута:

/**
 * Взаимодействие с адресом пользователя.
 */
protected function address(): Attribute
{
    return Attribute::make(
        get: fn (mixed $value, array $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
    )->withoutObjectCaching();
}

#Определение мутатора

Мутатор преобразует значение атрибута Eloquent при его установке. Чтобы определить мутатор, можно передать аргумент set при определении атрибута. Давайте определим мутатор для атрибута first_name. Этот мутатор будет автоматически вызван при попытке установить значение атрибута first_name в модели:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Взаимодействие с именем пользователя.
     */
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
            set: fn (string $value) => strtolower($value),
        );
    }
}

Замыкание мутатора получит значение, которое устанавливается в атрибут, что позволяет вам обработать значение и вернуть изменённое значение. Чтобы использовать мутатор, достаточно установить атрибут first_name у модели Eloquent:

use App\Models\User;

$user = User::find(1);

$user->first_name = 'Sally';

В этом примере обратный вызов set будет вызван со значением Sally. Мутатор применит функцию strtolower к имени и установит полученное значение во внутренний массив $attributes модели.

#Изменение нескольких атрибутов

Иногда мутатор должен установить несколько атрибутов в базовой модели. Для этого можно вернуть массив из замыкания set. Каждый ключ в массиве должен соответствовать атрибуту / столбцу базы данных, связанному с моделью:

use App\Support\Address;
use Illuminate\Database\Eloquent\Casts\Attribute;

/**
 * Взаимодействие с адресом пользователя.
 */
protected function address(): Attribute
{
    return Attribute::make(
        get: fn (mixed $value, array $attributes) => new Address(
            $attributes['address_line_one'],
            $attributes['address_line_two'],
        ),
        set: fn (Address $value) => [
            'address_line_one' => $value->lineOne,
            'address_line_two' => $value->lineTwo,
        ],
    );
}

#Кастинг атрибутов

Кастинг атрибутов предоставляет функциональность, похожую на аксессоры и мутаторы, без необходимости определять дополнительные методы в вашей модели. Вместо этого свойство $casts модели предоставляет удобный способ преобразования атрибутов в распространённые типы данных.

Свойство $casts должно быть массивом, где ключ — имя атрибута, который нужно кастить, а значение — тип, в который нужно преобразовать столбец. Поддерживаемые типы кастинга:

  • array
  • AsStringable::class
  • boolean
  • collection
  • date
  • datetime
  • immutable_date
  • immutable_datetime
  • decimal:<precision>
  • double
  • encrypted
  • encrypted:array
  • encrypted:collection
  • encrypted:object
  • float
  • hashed
  • integer
  • object
  • real
  • string
  • timestamp

Для демонстрации кастинга атрибутов давайте приведём атрибут is_admin, который хранится в базе как целое число (0 или 1), к булевому значению:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Атрибуты, которые должны быть приведены к типам.
     *
     * @var array
     */
    protected $casts = [
        'is_admin' => 'boolean',
    ];
}

После определения кастинга атрибут is_admin всегда будет приводиться к булевому типу при доступе, даже если исходное значение в базе хранится как целое число:

$user = App\Models\User::find(1);

if ($user->is_admin) {
    // ...
}

Если нужно добавить новый временный кастинг во время выполнения, можно использовать метод mergeCasts. Эти определения кастов будут добавлены к уже существующим в модели:

$user->mergeCasts([
    'is_admin' => 'integer',
    'options' => 'object',
]);
Внимание

Атрибуты со значением null не будут приведены к типу. Кроме того, никогда не следует определять кастинг (или атрибут) с именем, совпадающим с названием отношения, или назначать кастинг для первичного ключа модели.

#Кастинг в Stringable

Вы можете использовать класс кастинга Illuminate\Database\Eloquent\Casts\AsStringable для приведения атрибута модели к fluent объекту Illuminate\Support\Stringable:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\AsStringable;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Атрибуты, которые должны быть приведены к типам.
     *
     * @var array
     */
    protected $casts = [
        'directory' => AsStringable::class,
    ];
}

#Кастинг в массив и JSON

Кастинг array особенно полезен при работе со столбцами, которые хранятся в виде сериализованного JSON. Например, если в базе данных есть поле типа JSON или TEXT, содержащее сериализованный JSON, добавление кастинга array к этому атрибуту автоматически десериализует его в PHP-массив при доступе через модель Eloquent:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Атрибуты, которые должны быть приведены к типам.
     *
     * @var array
     */
    protected $casts = [
        'options' => 'array',
    ];
}

После определения кастинга вы можете получить доступ к атрибуту options, и он автоматически будет десериализован из JSON в PHP-массив. При установке значения атрибута options данный массив автоматически сериализуется обратно в JSON для хранения:

use App\Models\User;

$user = User::find(1);

$options = $user->options;

$options['key'] = 'value';

$user->options = $options;

$user->save();

Чтобы обновить одно поле JSON-атрибута более кратким синтаксисом, вы можете сделать атрибут массово назначаемым и использовать оператор -> при вызове метода update:

$user = User::find(1);

$user->update(['options->key' => 'value']);

#Кастинг в ArrayObject и Collection

Хотя стандартный кастинг array подходит для многих случаев, у него есть недостатки. Поскольку кастинг array возвращает примитивный тип, нельзя напрямую изменять отдельные элементы массива. Например, следующий код вызовет ошибку PHP:

$user = User::find(1);

$user->options['key'] = $value;

Чтобы решить эту проблему, Laravel предлагает кастинг AsArrayObject, который приводит JSON-атрибут к классу ArrayObject. Эта функция реализована с помощью пользовательского кастинга, который позволяет Laravel интеллектуально кэшировать и преобразовывать изменённый объект так, чтобы можно было изменять отдельные элементы без ошибок PHP. Чтобы использовать кастинг AsArrayObject, просто назначьте его атрибуту:

use Illuminate\Database\Eloquent\Casts\AsArrayObject;

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'options' => AsArrayObject::class,
];

Аналогично, Laravel предлагает кастинг AsCollection, который приводит JSON-атрибут к экземпляру Laravel Collection:

use Illuminate\Database\Eloquent\Casts\AsCollection;

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'options' => AsCollection::class,
];

Если вы хотите, чтобы кастинг AsCollection создавал экземпляр пользовательского класса коллекции вместо базового класса Laravel, вы можете указать имя класса коллекции в качестве аргумента кастинга:

use App\Collections\OptionCollection;
use Illuminate\Database\Eloquent\Casts\AsCollection;

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'options' => AsCollection::class.':'.OptionCollection::class,
];

#Кастинг дат

По умолчанию Eloquent приводит столбцы created_at и updated_at к экземплярам Carbon, который расширяет класс PHP DateTime и предоставляет множество полезных методов. Вы можете кастить дополнительные атрибуты дат, определяя дополнительные касты дат в массиве $casts вашей модели. Обычно даты следует кастить с помощью типов datetime или immutable_datetime.

При определении кастинга date или datetime вы можете также указать формат даты. Этот формат будет использоваться при сериализации модели в массив или JSON:

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'created_at' => 'datetime:Y-m-d',
];

Когда столбец кастится как дата, вы можете установить соответствующий атрибут модели в UNIX timestamp, строку даты (Y-m-d), строку даты и времени или экземпляр DateTime / Carbon. Значение даты будет корректно преобразовано и сохранено в базе данных.

Вы можете настроить формат сериализации по умолчанию для всех дат модели, определив метод serializeDate в вашей модели. Этот метод не влияет на формат даты при сохранении в базе данных:

/**
 * Подготовить дату для сериализации в массив / JSON.
 */
protected function serializeDate(DateTimeInterface $date): string
{
    return $date->format('Y-m-d');
}

Чтобы указать формат, который должен использоваться при сохранении дат модели в базе данных, определите свойство $dateFormat в вашей модели:

/**
 * Формат хранения столбцов даты модели.
 *
 * @var string
 */
protected $dateFormat = 'U';

#Кастинг дат, сериализация и часовые пояса

По умолчанию касты date и datetime сериализуют даты в строку UTC ISO-8601 (YYYY-MM-DDTHH:MM:SS.uuuuuuZ), независимо от часового пояса, указанного в конфигурации timezone вашего приложения. Настоятельно рекомендуется всегда использовать этот формат сериализации и хранить даты приложения в часовом поясе UTC, не изменяя значение timezone в конфигурации приложения с его значения по умолчанию UTC. Последовательное использование часового пояса UTC во всём приложении обеспечит максимальную совместимость с другими библиотеками для работы с датами на PHP и JavaScript.

Если к касту date или datetime применён пользовательский формат, например datetime:Y-m-d H:i:s, то при сериализации даты будет использоваться внутренний часовой пояс экземпляра Carbon. Обычно это часовой пояс, указанный в конфигурации timezone вашего приложения.

#Кастинг Enum

Eloquent также позволяет кастить значения атрибутов к PHP Enum. Для этого укажите атрибут и Enum, к которому нужно кастить, в массиве $casts вашей модели:

use App\Enums\ServerStatus;

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'status' => ServerStatus::class,
];

После определения кастинга атрибут будет автоматически приводиться к Enum и обратно при взаимодействии с ним:

if ($server->status == ServerStatus::Provisioned) {
    $server->status = ServerStatus::Ready;

    $server->save();
}

#Кастинг массивов Enum

Иногда нужно хранить массив значений Enum в одном столбце модели. Для этого можно использовать касты AsEnumArrayObject или AsEnumCollection, предоставляемые Laravel:

use App\Enums\ServerStatus;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'statuses' => AsEnumCollection::class.':'.ServerStatus::class,
];

#Зашифрованный кастинг

Кастинг encrypted шифрует значение атрибута модели с помощью встроенных возможностей шифрования Laravel. Кроме того, касты encrypted:array, encrypted:collection, encrypted:object, AsEncryptedArrayObject и AsEncryptedCollection работают аналогично своим незашифрованным аналогам, но, как и ожидалось, значение при хранении в базе данных шифруется.

Поскольку итоговая длина зашифрованного текста непредсказуема и больше, чем у исходного текста, убедитесь, что соответствующий столбец базы данных имеет тип TEXT или больше. Кроме того, так как значения шифруются в базе, вы не сможете выполнять запросы или поиск по зашифрованным атрибутам.

#Ротация ключей

Как известно, Laravel шифрует строки с использованием значения key из конфигурации app вашего приложения. Обычно это значение соответствует переменной окружения APP_KEY. Если вам нужно сменить ключ шифрования приложения, необходимо вручную повторно зашифровать ваши зашифрованные атрибуты с новым ключом.

#Кастинг во время запроса

Иногда нужно применять касты во время выполнения запроса, например, при выборе сырых значений из таблицы. Рассмотрим следующий запрос:

use App\Models\Post;
use App\Models\User;

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->get();

Атрибут last_posted_at в результатах этого запроса будет простой строкой. Было бы удобно применить к нему кастинг datetime во время выполнения запроса. К счастью, это можно сделать с помощью метода withCasts:

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
            ->whereColumn('user_id', 'users.id')
])->withCasts([
    'last_posted_at' => 'datetime'
])->get();

#Пользовательские касты

Laravel предоставляет множество встроенных полезных типов кастов, однако иногда может потребоваться определить собственные типы кастов. Чтобы создать каст, выполните Artisan-команду make:cast. Новый класс кастинга будет помещён в директорию app/Casts:

php artisan make:cast Json

Все пользовательские классы кастов реализуют интерфейс CastsAttributes. Классы, реализующие этот интерфейс, должны определить методы get и set. Метод get отвечает за преобразование сырого значения из базы в кастовое значение, а метод set должен преобразовывать кастовое значение в сырое, пригодное для хранения в базе. В качестве примера мы переопределим встроенный каст json как пользовательский:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class Json implements CastsAttributes
{
    /**
     * Привести заданное значение.
     *
     * @param  array<string, mixed>  $attributes
     * @return array<string, mixed>
     */
    public function get(Model $model, string $key, mixed $value, array $attributes): array
    {
        return json_decode($value, true);
    }

    /**
     * Подготовить заданное значение для хранения.
     *
     * @param  array<string, mixed>  $attributes
     */
    public function set(Model $model, string $key, mixed $value, array $attributes): string
    {
        return json_encode($value);
    }
}

После определения пользовательского кастинга вы можете прикрепить его к атрибуту модели, используя имя класса:

<?php

namespace App\Models;

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

class User extends Model
{
    /**
     * Атрибуты, которые должны быть приведены к типам.
     *
     * @var array
     */
    protected $casts = [
        'options' => Json::class,
    ];
}

#Кастинг в объекты-значения

Вы не ограничены кастингом в примитивные типы. Вы также можете кастить значения в объекты. Определение пользовательских кастов, которые кастят значения в объекты, очень похоже на кастинг в примитивы, но метод set должен возвращать массив пар ключ / значение, которые будут использоваться для установки сырых значений в модель.

В качестве примера мы определим пользовательский класс кастинга, который объединяет несколько значений модели в один объект-значение Address. Предположим, что объект Address имеет два публичных свойства: lineOne и lineTwo:

<?php

namespace App\Casts;

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

class Address implements CastsAttributes
{
    /**
     * Привести заданное значение.
     *
     * @param  array<string, mixed>  $attributes
     */
    public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject
    {
        return new AddressValueObject(
            $attributes['address_line_one'],
            $attributes['address_line_two']
        );
    }

    /**
     * Подготовить заданное значение для хранения.
     *
     * @param  array<string, mixed>  $attributes
     * @return array<string, string>
     */
    public function set(Model $model, string $key, mixed $value, array $attributes): array
    {
        if (! $value instanceof AddressValueObject) {
            throw new InvalidArgumentException('Переданное значение не является экземпляром Address.');
        }

        return [
            'address_line_one' => $value->lineOne,
            'address_line_two' => $value->lineTwo,
        ];
    }
}

При кастинге в объекты-значения любые изменения, внесённые в объект-значение, автоматически синхронизируются с моделью перед её сохранением:

use App\Models\User;

$user = User::find(1);

$user->address->lineOne = 'Обновлённое значение адреса';

$user->save();
Примечание

Если вы планируете сериализовать ваши модели Eloquent, содержащие объекты-значения, в JSON или массивы, следует реализовать интерфейсы Illuminate\Contracts\Support\Arrayable и JsonSerializable в объекте-значении.

#Кэширование объектов-значений

При разрешении атрибутов, кастящихся в объекты-значения, они кэшируются Eloquent. Поэтому при повторном доступе к атрибуту возвращается тот же экземпляр объекта.

Если вы хотите отключить поведение кэширования объектов в пользовательских классах кастов, можете объявить публичное свойство withoutObjectCaching в вашем классе кастинга:

class Address implements CastsAttributes
{
    public bool $withoutObjectCaching = true;

    // ...
}

#Сериализация массива / JSON

Когда модель Eloquent преобразуется в массив или JSON с помощью методов toArray и toJson, ваши объекты-значения из пользовательских кастов обычно сериализуются, если они реализуют интерфейсы Illuminate\Contracts\Support\Arrayable и JsonSerializable. Однако при использовании объектов-значений из сторонних библиотек вы можете не иметь возможности добавить эти интерфейсы в объект.

Поэтому вы можете указать, что ваш пользовательский класс кастинга будет отвечать за сериализацию объекта-значения. Для этого ваш класс кастинга должен реализовывать интерфейс Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes. Этот интерфейс требует наличия метода serialize, который должен возвращать сериализованное представление объекта-значения:

/**
 * Получить сериализованное представление значения.
 *
 * @param  array<string, mixed>  $attributes
 */
public function serialize(Model $model, string $key, mixed $value, array $attributes): string
{
    return (string) $value;
}

#Входящий кастинг

Иногда нужно написать пользовательский класс кастинга, который преобразует только значения, устанавливаемые в модель, и не выполняет никаких операций при получении атрибутов из модели.

Входящие касты должны реализовывать интерфейс CastsInboundAttributes, который требует определения только метода set. Команда Artisan make:cast может быть вызвана с опцией --inbound для генерации класса входящего кастинга:

php artisan make:cast Hash --inbound

Классический пример входящего кастинга — "хеширование". Например, можно определить каст, который хеширует входящие значения с помощью заданного алгоритма:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Database\Eloquent\Model;

class Hash implements CastsInboundAttributes
{
    /**
     * Создать новый экземпляр класса кастинга.
     */
    public function __construct(
        protected string|null $algorithm = null,
    ) {}

    /**
     * Подготовить заданное значение для хранения.
     *
     * @param  array<string, mixed>  $attributes
     */
    public function set(Model $model, string $key, mixed $value, array $attributes): string
    {
        return is_null($this->algorithm)
                    ? bcrypt($value)
                    : hash($this->algorithm, $value);
    }
}

#Параметры кастов

При прикреплении пользовательского кастинга к модели параметры кастинга можно указать, отделив их от имени класса символом : и разделив запятыми несколько параметров. Параметры будут переданы в конструктор класса кастинга:

/**
 * Атрибуты, которые должны быть приведены к типам.
 *
 * @var array
 */
protected $casts = [
    'secret' => Hash::class.':sha256',
];

#Castables

Вы можете позволить вашим объектам-значениям определять собственные пользовательские классы кастинга. Вместо того чтобы прикреплять класс кастинга к модели, вы можете прикрепить класс объекта-значения, реализующий интерфейс Illuminate\Contracts\Database\Eloquent\Castable:

use App\ValueObjects\Address;

protected $casts = [
    'address' => Address::class,
];

Объекты, реализующие интерфейс Castable, должны определить метод castUsing, который возвращает имя класса кастера, отвечающего за кастинг из и в класс Castable:

<?php

namespace App\ValueObjects;

use Illuminate\Contracts\Database\Eloquent\Castable;
use App\Casts\Address as AddressCast;

class Address implements Castable
{
    /**
     * Получить имя класса кастера для кастинга из / в этот тип.
     *
     * @param  array<string, mixed>  $arguments
     */
    public static function castUsing(array $arguments): string
    {
        return AddressCast::class;
    }
}

При использовании классов Castable вы всё равно можете передавать аргументы в определении $casts. Аргументы будут переданы в метод castUsing:

use App\ValueObjects\Address;

protected $casts = [
    'address' => Address::class.':argument',
];

#Castables и анонимные классы кастинга

Объединив "castables" с PHP анонимными классами, вы можете определить объект-значение и его логику кастинга как единый кастабельный объект. Для этого верните анонимный класс из метода castUsing вашего объекта-значения. Анонимный класс должен реализовывать интерфейс CastsAttributes:

<?php

namespace App\ValueObjects;

use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Address implements Castable
{
    // ...

    /**
     * Получить класс кастера для кастинга из / в этот тип.
     *
     * @param  array<string, mixed>  $arguments
     */
    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,
                ];
            }
        };
    }
}