[14.x] Add defaults() method to Eloquent models

Merged
JA
21d ago
+104-0

We have a casts() method (which is the default in 2026), but there's no alternative for the attributes array (defaults)

This means feature flagging a cast is easy with casts(). but, flagging a default means overriding __construct something like:

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

I would like the ability to have a method for this, for consistency and ease of flagging, the ability to run logic at runtime, etc... So this PR adds a defaults() method as the method equivalent of $attributes:

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

Like $attributes, these are raw values, so they aren't cast or passed through mutators.

defaults() takes priority over the $attributes array, as casts() does vs $casts.

I can roll a trait locally if you hate this, but had to try.

I've targeted 14.x incase existing applications have a defaults method. Open to feedback and renaming etc. Feel free to complete change it style wise, but I bring the idea!