Laravel 14 добавляет метод defaults() для моделей Eloquent

Eloquent позволяет задавать значения атрибутов по умолчанию с помощью свойства $attributes. Это удобно для фиксированных значений, но PHP разрешает использовать в значениях свойств только константные выражения. Поэтому вызвать там config(), now() или проверить feature flag нельзя.

В Laravel 14 появляется метод defaults(), который возвращает массив значений по умолчанию из кода, выполняемого при создании модели.

До и после

Рассмотрим модель подписки, для которой новым подпискам назначается пробный период в 14 дней, а при включенном feature flag extended-trials он увеличивается до 30 дней.

При использовании $attributes проверку флага приходится выполнять в конструкторе:

class Subscription extends Model
{
    protected $attributes = [
        'cancelled' => false,
        'trial_days' => 14,
    ];

    public function __construct(array $attributes = [])
    {
        if (Feature::active('extended-trials')) {
            $this->attributes['trial_days'] = 30;
        }

        parent::__construct($attributes);
    }
}

В Laravel 14 ту же логику можно разместить в defaults():

class Subscription extends Model
{
    protected function defaults(): array
    {
        return [
            'cancelled' => false,
            'trial_days' => Feature::active('extended-trials') ? 30 : 14,
            'expires_at' => now()->addDays(config('billing.grace_period'))->toDateTimeString(),
        ];
    }
}

Теперь при создании модели:

$subscription = new Subscription;

значение trial_days будет равно 30 при активном флаге и 14 в противном случае:

$subscription->trial_days; // 30 when the flag is active, 14 otherwise

В Laravel 11 аналогичное изменение произошло с приведением типов. В шаблоне приложения свойство $casts заменили методом casts(). Теперь defaults() предоставляет такой же вариант для $attributes.

Как работает defaults()

Конструктор модели вызывает defaults() и объединяет полученный массив с $attributes. Порядок операций определяет, какое значение будет использовано:

  1. Сначала применяются значения из свойства $attributes.

  2. Затем поверх них объединяется результат defaults(), поэтому он имеет приоритет при совпадении ключей.

  3. Атрибуты, переданные в new, create() или fill(), применяются последними и имеют приоритет над обоими источниками.

Например:

$subscription = Subscription::create(['trial_days' => 60]);

В результате значение будет равно 60:

$subscription->trial_days; // 60

Значения по умолчанию объединяются с атрибутами до синхронизации исходных атрибутов модели. Поэтому они не считаются изменениями и не помечаются как dirty. При этом они записываются в базу данных при вставке модели, так же как значения из $attributes.

Сырые значения

Как и значения из $attributes, значения, возвращаемые defaults(), являются сырыми. Они не проходят через casts или mutators. Поэтому возвращать их нужно в том виде, в котором они должны храниться в базе данных:

protected function defaults(): array
{
    return [
        // Для столбца с cast в array ожидается JSON-строка
        'settings' => json_encode(['theme' => 'light']),

        // Столбец с датой
        'expires_at' => now()->addDays(30)->toDateTimeString(),
    ];
}

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

Когда вызывается defaults()

Метод defaults() вызывается каждый раз при создании экземпляра модели. Это касается и моделей, загружаемых из базы данных.

Eloquent создает новый экземпляр модели для каждой полученной строки, поэтому defaults() выполняется один раз для каждой строки. После этого значения из самой строки заменяют значения по умолчанию.

Обновление до Laravel 14

Метод появился именно в Laravel 14, поскольку в существующей модели уже может находиться метод с именем defaults(). После обновления Laravel конструктор будет вызывать его и объединять возвращаемый массив с атрибутами модели.

Перед обновлением рекомендуется найти такие методы в моделях:

grep -rn "function defaults" app/Models

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

Важно: Изменение уже добавлено в ветку master, но до выхода Laravel 14 его поведение еще может измениться.

Похожие статьи

Рекомендательные технологии Подробнее
Разработка и инструменты • 7 месяцев назад

Логирование активности пользователей в Laravel с Activity Log v5

Обзор обновлений пакета spatie/laravel-activitylog v5 для Laravel. Рассматриваются новый трейт, буферизация логов, API для указания инициатора действий и изменения структуры хранения данных.

Screenshot: создание изображений и PDF из PHP и Laravel

Screenshot позволяет создавать изображения PNG и JPEG, а также PDF из HTML, Blade-представлений и веб-страниц в приложениях Laravel. Пакет поддерживает выбор отдельных элементов страницы, сохранение результатов в файловые хранилища и использование локального Chrome или удаленного Gotenberg.

Разработка и инструменты • 8 месяцев назад

Как правильно хранить деньги в коде: без потерь, округлений и боли

Как правильно хранить денежные значения в базе данных и коде. Сравниваем целые минорные единицы и Decimal, объясняем, почему float использовать нельзя.