Overview
Every time an Eloquent model is instantiated it runs two phases: boot (once per class) and initialize (once per instance). When you define specific methods in a trait, Eloquent automatically discovers and calls them. Adding the trait to a model is all that’s needed.
Laravel itself uses this pattern extensively in traits like SoftDeletes, HasUuids, and HasUlids.
boot vs. initialize
| bootXxx() | initializeXxx() |
|---|
| When called | Once when the model class first boots | Every time an instance is created |
| Method type | static | Instance method |
| Typical use | Register event listeners, add global scopes | Set default attribute values, merge casts |
Naming Conventions
bootXxx()
Define a static method prefixed with boot followed by the trait’s class basename. Eloquent calls it once per class lifecycle.
namespace App\Models\Concerns;
trait HasSlug
{
public static function bootHasSlug(): void
{
static::creating(function ($model) {
if (empty($model->slug)) {
$model->slug = str($model->name)->slug()->toString();
}
});
}
}
Using the trait in a model:
use App\Models\Concerns\HasSlug;
class Article extends Model
{
use HasSlug;
// bootHasSlug() is called automatically
}
initializeXxx()
Define an instance method prefixed with initialize followed by the trait’s class basename. Eloquent calls it every time a new instance is created.
namespace App\Models\Concerns;
trait HasDefaultStatus
{
public function initializeHasDefaultStatus(): void
{
$this->attributes['status'] ??= 'draft';
}
}
Dynamically merging casts is another common use:
trait HasMetadata
{
public function initializeHasMetadata(): void
{
$this->mergeCasts([
'metadata' => 'array',
]);
}
}
PHP Attributes
Since Laravel 11, you can use PHP Attributes instead of relying on naming conventions.
| Attribute | Purpose |
|---|
#[Boot] | Mark a static method to run once when the class boots |
#[Initialize] | Mark an instance method to run on every instantiation |
use Illuminate\Database\Eloquent\Attributes\Boot;
use Illuminate\Database\Eloquent\Attributes\Initialize;
trait HasAuditLog
{
#[Boot]
public static function registerAuditListeners(): void
{
static::created(function ($model) {
AuditLog::record('created', $model);
});
static::updated(function ($model) {
AuditLog::record('updated', $model);
});
}
#[Initialize]
public function setAuditDefaults(): void
{
$this->attributes['audit_enabled'] ??= true;
}
}
Using PHP Attributes frees your method names from the trait-name constraint. You can also define multiple #[Boot] or #[Initialize] methods within the same trait.
Practical Examples
Adding a Global Scope Automatically
trait HasTenant
{
public static function bootHasTenant(): void
{
static::addGlobalScope(new TenantScope);
}
}
Merging Default Casts
trait HasJsonSettings
{
public function initializeHasJsonSettings(): void
{
$this->mergeCasts([
'settings' => 'array',
]);
}
}
Auto-archiving on Delete
trait Archivable
{
#[Boot]
public static function bootArchivable(): void
{
static::deleting(function ($model) {
Archive::store($model->toArray());
});
}
}
Examples in the Framework
Laravel’s own traits demonstrate the pattern clearly:
| Trait | Method | What it does |
|---|
SoftDeletes | bootSoftDeletes() | Registers the SoftDeletingScope global scope |
HasUlids | bootHasUlids() | Sets ULID on the creating event |
HasUuids | bootHasUuids() | Sets UUID on the creating event |
Reading these implementations is a great way to understand the pattern in depth.
Caveats
Clearing Booted Models in Tests
Because boot* runs only once per class, tests that alter model behavior may need to reset the boot cache:
protected function tearDown(): void
{
Model::clearBootedModels();
parent::tearDown();
}
Execution Order
When a model uses multiple traits, bootXxx() and initializeXxx() are called in PHP’s trait resolution order (declaration order). Be mindful of ordering when traits depend on each other.
Summary
| Pattern | How to define | When to use |
|---|
| Class boot | static bootXxx() or #[Boot] | Event listeners, global scopes |
| Instance init | initializeXxx() or #[Initialize] | Default values, dynamic casts |
Implementing bootXxx() and initializeXxx() in a trait lets you inject behavior into any Eloquent model with a single use statement — the go-to pattern for package authors.