Laravel - 使用 Eloquent 的 boot trait 建立 UUID

前言

在找尋與 UUID 相關的套件時,意外地發現了 Laravel Eloquent 內建的 boot trait 的機制,這是一個非常方便的功能,可以在 Model 在建立之前,自動執行某些操作,並且能夠有效的重複利用程式碼。

假設有一個 User 的 Eloquent Model,在建立新的 User 時,Model 需要建立對應的 UUID,可能會這麼做:

1
2
3
4
5
6
7
8
9
10
11
class User extends Model
{
public static function boot()
{
parent::boot();

static::creating(function (Model $model) {
$model->uuid = Str::uuid();
});
}
}

Eloquent Boot Trait

接著,如果還有另一個 Order 的 Eloquent Model 也需要建立 UUID 時,可能就要再複製同樣的程式碼,最後造成相同的邏輯重複出現在各個 Model 當中,這很容易造成維護上的困難。

為了避免重複的程式碼出現,可以使用 Eloquent 內建的 boot 功能

可以先看一下 Model 相關方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

// src/Illuminate/Database/Eloquent/Model.php

/**
* Bootstrap the model and its traits.
*
* @return void
*/
protected static function boot()
{
static::bootTraits();
}

/**
* Boot all of the bootable traits on the model.
*
* @return void
*/
protected static function bootTraits()
{
$class = static::class;

$booted = [];

static::$traitInitializers[$class] = [];

foreach (class_uses_recursive($class) as $trait) {
$method = 'boot'.class_basename($trait);

if (method_exists($class, $method) && ! in_array($method, $booted)) {
forward_static_call([$class, $method]);

$booted[] = $method;
}

if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
static::$traitInitializers[$class][] = $method;

static::$traitInitializers[$class] = array_unique(
static::$traitInitializers[$class]
);
}
}
}

可以大概看出這裡有做一些操作

  1. 檢查有沒有 boot 開頭的 trait
  2. 如果存在則執行它

因此,可以透過這樣的機制為 Eloquent 做一些自動化的操作

所以調整一下開始的範例並使用 trait 的方式

1
2
3
4
5
6
7
8
9
trait HasUuid
{
public static function bootHasUuid()
{
static::creating(function (Model $model) {
$model->uuid = Str::uuid();
});
}
}

並將這個 trait 套用到 Model 上

1
2
3
4
Class User extends Model
{
use HasUuid;
}
1
2
3
4
Class User extends Model
{
use HasUuid;
}

這樣就可以快速地讓 Model 擁有產生 UUID 的功能了

總結

在 Laravel 中,您可以透過使用 Eloquent 的 boot 機制和 trait 來有效地減少重複的程式碼,只需要在對應的 Trait 中以 bootTraitName 的方式命名即可,Eloquent 就會在建立時自動地使用的使用這個方法。這樣不僅可以提升程式碼的可讀性,還可以讓您更加輕鬆地管理多個類似的功能。

Reference