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

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
PHP Security Releases Fix Two Critical RCE Vulnerabilities image

PHP Security Releases Fix Two Critical RCE Vulnerabilities

Read article
Prevent Stale Database Reads in Laravel 13.38 image

Prevent Stale Database Reads in Laravel 13.38

Read article
Critical PHP Security Release Coming Tomorrow (October 21) image

Critical PHP Security Release Coming Tomorrow (October 21)

Read article
Critical Token Guard Auth Bypass Fixed in Laravel 13.29 and 12.68 image

Critical Token Guard Auth Bypass Fixed in Laravel 13.29 and 12.68

Read article
Run Large PlanetScale Queries in Laravel With DB::olap() image

Run Large PlanetScale Queries in Laravel With DB::olap()

Read article
Fluent Rule Builders for All Laravel Validation Rules image

Fluent Rule Builders for All Laravel Validation Rules

Read article