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 →
Laravel AI SDK and Laravel MCP Security Fixes: Update Now image

Laravel AI SDK and Laravel MCP Security Fixes: Update Now

Read article
Mailbox for Laravel: Preview and Test Rendered Email image

Mailbox for Laravel: Preview and Test Rendered Email

Read article
Count Worker Crashes as Job Exceptions in Laravel 13.34 image

Count Worker Crashes as Job Exceptions in Laravel 13.34

Read article
Elastic Bridge: Eloquent-Style Queries for Elasticsearch and OpenSearch image

Elastic Bridge: Eloquent-Style Queries for Elasticsearch and OpenSearch

Read article
Laravel Release Cycle: Versions, Support Policy, and Dates image

Laravel Release Cycle: Versions, Support Policy, and Dates

Read article
Unlearn.dev goes free for a weekend, October 10 and 11 image

Unlearn.dev goes free for a weekend, October 10 and 11

Read article