How to Use Bootable Traits in Laravel

Eduar Bastidas • July 6, 2025

tips

Bootable traits are the stealth feature that lets you share event‑driven behavior across Eloquent models without littering every class with boot methods or external observers. Any trait that adds a static bootTraitName() hook is automatically executed the moment Laravel initializes the model—no extra wiring required.

Below you'll find a quick refresher on how the mechanism works, followed by a practical example that encrypts sensitive attributes. Swap the encryption logic for anything—audit stamps, geocoding, feature flags—and the pattern stays identical.


Why Bootable Traits Matter

Under the hood, Eloquent loops through class_uses_recursive($model) and calls any boot{Trait} static methods it finds. That's the secret sauce that fires your trait code exactly once per request, right after the model's own boot().

You can see this magic happen in Laravel's source code: Model.php line 347

Laravel itself uses bootable traits extensively. The most familiar example is SoftDeletes, which implements bootSoftDeletes() to automatically add global scopes and handle soft deletion behavior across any model that uses it. You can see this in action: SoftDeletes.php line 31


Case Study: EncryptsAttributes Trait

Want to keep api_token or secret_key columns safe? A bootable trait can intercept the saving event and transparently run Crypt::encryptString(), while providing an opt‑in helper for decrypting on demand.

1<?php
2namespace App\Traits;
3 
4use Illuminate\Support\Facades\Crypt;
5 
6trait EncryptsAttributes
7{
8 /**
9 * Auto‑encrypt configured attributes just before the model is persisted.
10 *
11 * @return void
12 */
13 public static function bootEncryptsAttributes(): void
14 {
15 static::saving(function ($model) {
16 foreach ($model->encryptable() as $key) {
17 if (! is_null($model->{$key})) {
18 $model->{$key} = Crypt::encryptString($model->{$key});
19 }
20 }
21 });
22 }
23 
24 /**
25 * Models must declare which columns need encryption.
26 *
27 * @return array<string>
28 */
29 abstract public function encryptable(): array;
30 
31 /**
32 * Manual decrypt helper for occasional plaintext access.
33 *
34 * @param string $key
35 * @return string|null
36 */
37 public function decryptAttribute(string $key): ?string
38 {
39 return is_null($this->{$key})
40 ? null
41 : Crypt::decryptString($this->{$key});
42 }
43}

Attaching the Trait to a Model

1<?php
2namespace App\Models;
3 
4use Illuminate\Database\Eloquent\Model;
5use App\Traits\EncryptsAttributes;
6 
7class ApiCredential extends Model
8{
9 use EncryptsAttributes;
10 
11 /**
12 * The attributes that are mass assignable.
13 *
14 * @var array<int, string>
15 */
16 protected $fillable = ['name', 'api_token', 'secret_key'];
17 
18 /**
19 * Tell the trait which columns to protect.
20 *
21 * @return array<string>
22 */
23 public function encryptable(): array
24 {
25 return ['api_token', 'secret_key'];
26 }
27}

That's it—no per‑model observers, no duplicated callbacks. When you create() or update() an ApiCredential, the sensitive fields are encrypted before hitting the database.


Reading Plaintext Safely

Need the raw token inside a service? Call:

1$plain = $credential->decryptAttribute('api_token');

Limit plaintext exposure—decrypt only inside trusted services, not in blade views that might leak logs.


Why the Pattern Scales


Extending the Idea

Bootable traits aren't limited to encryption. Use the exact same scaffold to:

If the logic belongs on every instance of a given model class, a bootable trait is often clearer than a universal observer.