最近我不得不在 Laravel 7 實現 通用唯一識別碼 ( UUIDs ),并遇到一些問題。我希望這帖子可為其他正在做相同事情的人解惑。
使用 UUIDs 的高級理由
A) 它們從你的 統一資源定位符 移除編號的 身份識別號 ,故用戶不能看到你的應用已創建多少確定的對象。例如:
https://myapp.com/api/users/5
對比:
https://myapp.com/api/users/0892b118-856e-4a15-af0c-66a3a4a28eed
B) 它們讓 身份識別號 遠難于猜測。這有益于安全性,但我們可能應當實現其他技術以防范之。
作為主鍵實現 UUIDs
如何改變數據庫遷移
首先,在數據庫遷移中,你要將當前自動遞增的 ID 字段替換為 UUIDs 。你還可以遵循以下方法:保留自動遞增 ID 并將 UUID 作為表中的附加字段,在用戶展示 URL 時使用 (在這種情況下,你將 ID 隱藏到模型中),但這不是我們能在這里做的。 讓我們看看假設的 employees 表是什么樣子的。
public?function?up() ????{ ????????Schema::create('employees',?function?(Blueprint?$table)?{ ????????????$table->uuid('id')->primary; ????????????$table->string('name'); ????????????$table->string('email')->unique(); ????????????$table->string('work_location')->nullable(); ????????????$table->timestamps(); ????????}); ????}
在這里,注意我們用 uuid() 替換了 normal id() ;并使其成為主鍵。
讓我們把它變成一種特質
接下來,我們可以實現 Laravel 生命周期掛鉤,以確保在創建此模型的新實例時分配了 UUID。我們可以直接在模型中編寫代碼,但是如果你要在多個模型中使用 UUID,我建議用 Trait (我在這篇開發文章中學到了這一點,非常感謝 Dev)。trait 基本上允許你創建功能,并通過 use 關鍵字調用它在多個模型中使用。
要創建新的 Trait,請創建一個 AppHttpTraits文件夾 (這僅僅是我的愛好,你也可以將其放到其他位置),并為 Trait 創建一個新文件。我們將調用文件 UsesUuid.php。
這是 trait 的具體代碼:
<?php namespace AppHttpTraits; use IlluminateSupportStr; trait UsesUuid { protected static function bootUsesUuid() { static::creating(function ($model) { if (! $model->getKey())?{ ????????$model->{$model->getKeyName()}?=?(string)?Str::uuid(); ??????} ????}); ??} ??public?function?getIncrementing() ??{ ??????return?false; ??} ??public?function?getKeyType() ??{ ??????return?'string'; ??} }
使用 IlluminateSupportStr 輕松生成 UUID.。getIncrementing () 方法告訴 Laravel 該模型的主鍵不會自增 (因為我們設置的是 false), 而 getKeyType () 方法告訴 Laravel 該模型的主鍵是字符串類型。bootUsesUuid () 方法允許我們使用 Laravel 強大的生命周期鉤子。你可以 在這來哦藕節更多詳細信息。基本上我們的代碼已經可以告訴 Laravel,當創建該模型的新實例時,為其設置 UUID 主鍵!
現在,我們可以使用 use 關鍵字在模型上輕松實現此特征。
<?php namespace App; use IlluminateDatabaseEloquentModel; ... class Employee extends Model { ... use AppHttpTraitsUsesUuid; ... }
將 UUID 引用為外鍵
要將表上的 UUID 引用為外鍵,只需更改表上外鍵字段的類型。如下…
?Schema::create('another_table',?function(Blueprint?$table)?{ ????????$table->id(); ????????$table->unsignedBigInteger('employee_id'); ????????$table->string('some_field'); ????????$table->foreign('employee_id') ????????????->references('id') ????????????->on('shifts') ????????????->onDelete('cascade'); ????});
… 我們在引用 employee_id 外鍵時創建了一個無符號大整型的數據類型,對此進行如下修改:
?Schema::create('another_table',?function(Blueprint?$table)?{ ????????$table->id(); ????????$table->uuid('employee_id'); ????????$table->string('some_field'); ????????$table->foreign('employee_id') ????????????->references('id') ????????????->on('shifts') ????????????->onDelete('cascade'); ????});
那樣簡單!還有一件事…
UUID 和多態關系
您可能會發現自己通過自己的操作或要引入的包以多態關系引用了該模型。在遷移中,該表可能看起來像這樣:
????public?function?up() ????{ ????????Schema::create('some_package_table',?function?(Blueprint?$table)? ????????{ ????????????$table->bigIncrements('id'); ????????????$table->morphs('model'); ????????????... ????????} ????}
在這里,morphs () 方法將在數據庫中創建兩個字段,即無符號大整型類型的 model_id 和字符串類型的 model_type。問題在于我們的模型現在使用的是 UUID 而不是遞增的整數 ID,因此這會給您帶來錯誤,并顯示類似以下內容::
Data?truncated?for?column?'model_id'?at?row?1
我們現在需要 model_id 字段來支持我們的新 UUID,它的類型是 CHAR (36)。別擔心!Laravel 讓這件事變得超級簡單,你不需要手動做這件事。只需將遷移更改為:
????public?function?up() ????{ ????????Schema::create('some_package_table',?function?(Blueprint?$table)? ????????{ ????????????$table->bigIncrements('id'); ????????????$table->uuidMorphs('model'); ????????????... ????????} ????}
愛 Laravel 的另一個原因!編碼愉快!
推薦教程:《Laravel教程》