Paul Redmond reported the change for Laravel News on October 1, 2026. Laravel 14 adds a protected defaults(): array method to Eloquent models. The $attributes property remains suitable for fixed values, but PHP allows only constant expressions in property defaults. Runtime calls such as config(), now(), and feature checks therefore need another place.
<?php
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(),
];
}
}The example uses a Subscription model. It keeps cancelled set to false and gives new subscriptions 14 trial days. Feature::active('extended-trials') can raise that value to 30. The method also calculates expires_at with now()->addDays(config('billing.grace_period'))->toDateTimeString(). The earlier approach checked the feature flag in __construct and changed trial_days before calling parent::__construct($attributes).
Eloquent merges defaults during construction. Values from $attributes are loaded first, defaults() overrides matching keys, and values supplied to new, create(), or fill() are applied last. Subscription::create(['trial_days' => 60]) therefore retains 60. The merge occurs before Eloquent synchronizes original attributes, so these defaults are not marked dirty. They are still inserted into the database like values from $attributes.
Values returned by defaults() are raw. Casts and mutators do not process them at this stage. A settings column with an array or JSON cast needs a JSON string such as json_encode(['theme' => 'light']). A date value can use now()->addDays(30)->toDateTimeString(). Returning a PHP array for an array or JSON cast causes reading the attribute to fail because the cast expects JSON.
The method runs whenever Eloquent constructs a model, including while hydrating rows from the database. Each retrieved row gets a new model instance, and the row's stored values then replace the generated defaults.
Laravel 11 made a comparable change for casts by moving the application skeleton from the $casts property to casts(). Laravel 14 introduces defaults() partly because an existing model may already define a method with that name. Developers should search with `grep -rn "function defaults" app/Models` and rename methods that do not return default attributes. Jack Bayliss contributed the change in PR #61688. It is merged into the master branch and may still change before Laravel 14 is released.




Comments
No comments yet — be the first.
Open the discussion
No account or password needed — just enter your e-mail and we’ll send you a one-time sign-in link. First time here? You’re set up automatically.
Your rating will be applied automatically after you sign in.
Check your inbox
We’ve sent a sign-in link to …. Open it on this device — this tab will sign you in automatically.
Nothing arrived? Check your spam folder — and mark the mail as "Not spam" so it lands in your inbox next time.