Модели Eloquent задают дефолтные значения атрибутов с помощью свойства $attributes . Это работает для фиксированных значений, но PHP позволяет использовать в дефолтных значениях свойств только константные выражения, поэтому вы не можете вызвать там config() , now() или проверить feature-флаг. В Laravel 14 добавлен метод defaults() , который возвращает тот же массив из кода, выполняющегося при создании модели.
#До и после
Возьмем модель подписки, которая дает новым подпискам 14-дневный пробный период, и 30 дней, когда активен feature-флаг extended-trials . В $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;
$subscription->trial_days; // 30 when the flag is active, 14 otherwise
Laravel 11 сделал аналогичный шаг для приведения типов (casts), когда структура приложения перешла от свойства $casts к методу casts() (Приведение типов моделей переходит на методы в Laravel 11). defaults() дает $attributes версию в виде метода.
#Как это работает
Конструктор модели вызывает defaults() и объединяет результат с $attributes . Порядок в конструкторе определяет, какое значение имеет приоритет:
- Сначала устанавливаются значения из свойства
$attributes - Поверх них накладывается
defaults(), поэтому он побеждает, если оба определяют один и тот же ключ - Атрибуты, которые вы передаете в
new,create()илиfill(), применяются в последнюю очередь, поэтому они переопределяют оба предыдущих варианта
$subscription = Subscription::create(['trial_days' => 60]);
$subscription->trial_days; // 60
Дефолтные значения объединяются до того, как модель синхронизирует свои исходные атрибуты, поэтому они не считаются измененными (dirty). Они все равно записываются в базу данных при сохранении модели, точно так же, как и $attributes .
#Сырые значения
Как и в случае с $attributes , значения из defaults() являются сырыми. Они не проходят через приведение типов или мутаторы. Возвращайте значение в том виде, в котором оно должно храниться в базе данных:
protected function defaults(): array
{
return [
// A column cast to `array` expects a JSON string
'settings' => json_encode(['theme' => 'light']),
// A date column
'expires_at' => now()->addDays(30)->toDateTimeString(),
];
}
Возврат массива PHP для колонки, приводимой к array или json , приведет к ошибке при чтении атрибута, так как приведение типов ожидает строку JSON.
#Когда выполняется defaults()
Метод defaults() выполняется каждый раз при создании экземпляра модели. Это относится и к моделям, загружаемым из базы данных. Eloquent создает каждую полученную строку как новый экземпляр модели, поэтому defaults() запускается один раз для каждой строки, а затем значения строки заменяют дефолтные.
#Обновление
Этот метод запланирован для Laravel 14, поскольку в существующей модели уже может быть метод с именем defaults() . В Laravel 14 конструктор вызовет его и объединит все, что он возвращает, с атрибутами. Перед обновлением выполните поиск такого метода в ваших моделях:
grep -rn "function defaults" app/Models
Переименуйте любой метод, который не предназначен для возврата дефолтных значений атрибутов.
Jack Bayliss добавил этот метод в пулл-реквесте #61688. Он слит в ветку master и может измениться до релиза Laravel 14.
Комментарии (0)
Пока нет комментариев — будьте первым.