News

Laravel 14 Adds a defaults() Method to Eloquent Models

Published
Laravel 14 Adds a defaults() Method to Eloquent Models image

Eloquent models set default attribute values with the $attributes property. It works for fixed values, but PHP only allows constant expressions in a property's default value, so you cannot call config(), now(), or a feature flag there. Laravel 14 adds a defaults() method that returns the same array from code that runs when the model is created.

Before and After

Take a subscription model that gives new subscriptions a 14-day trial, and 30 days when an extended-trials feature flag is active. With $attributes, the flag has to be checked in the constructor:

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);
}
}

In Laravel 14, the same logic goes in 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 made the same move for casts, when the application skeleton switched from the $casts property to a casts() method (Model Casts are moving to methods in Laravel 11). defaults() gives $attributes a method version too.

How It Works

The model constructor calls defaults() and merges the result into $attributes. The order in the constructor decides which value wins:

  1. Values from the $attributes property are set first
  2. defaults() is merged over them, so it wins when both define the same key
  3. The attributes you pass to new, create(), or fill() are applied last, so they win over both
$subscription = Subscription::create(['trial_days' => 60]);
 
$subscription->trial_days; // 60

Defaults are merged before the model syncs its original attributes, so they do not count as dirty changes. They are still written to the database when the model is inserted, the same as $attributes.

Raw Values

Like $attributes, the values from defaults() are raw. They do not go through casts or mutators. Return the value as it would be stored in the database:

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(),
];
}

Returning a PHP array for a column cast to array or json fails when the attribute is read, because the cast expects a JSON string.

When defaults() Runs

The defaults() method runs every time a model instance is constructed. That includes models loaded from the database. Eloquent builds each retrieved row with a new model instance, so defaults() runs once per row, and then the row's values replace the defaults.

Upgrading

The method targets Laravel 14 because an existing model might already have a method named defaults(). In Laravel 14, the constructor calls it and merges whatever it returns into the attributes. Before upgrading, search your models for one:

grep -rn "function defaults" app/Models

Rename any method that is not meant to return default attribute values.

Jack Bayliss contributed the method in #61688. It is merged into the master branch, and it can change before Laravel 14 is released.

Further Reading

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

laravelcloud logo
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud

The latest

View all →
Running Laravel's Scheduler on Multiple Servers image

Running Laravel's Scheduler on Multiple Servers

Read article
Laravel Fake Assertions Now Accept Property Arrays image

Laravel Fake Assertions Now Accept Property Arrays

Read article
Route::query() and Model defaults() in Laravel 13.35 image

Route::query() and Model defaults() in Laravel 13.35

Read article
Laravel AI SDK 1.1 Adds Agent Skills and Cohere Chat image

Laravel AI SDK 1.1 Adds Agent Skills and Cohere Chat

Read article
VMPal: Give Your AI Agent a Whole Computer image

VMPal: Give Your AI Agent a Whole Computer

Read article
Synapse: A Dev Dashboard for Laravel AI SDK Agents image

Synapse: A Dev Dashboard for Laravel AI SDK Agents

Read article