現在我們可以用各種方法將資料讀取出來,不過通常讀取後還要將資料做一些轉換才適用,舉個例子像是 boolean 欄位在 SQL 資料庫裡存的是數字的 0 跟 1,直接讀出來的話也會是 0/1 ,通常我們會想要將這種欄位轉換成 false/true 顯示。
對於這種情況 Eloquent 就可以在 Model 中指定欄位做資料轉換,這樣讀取出來的資料就會全部自行轉換成一樣的格式。
反過來也可以定義當寫入資料時事先對資料做轉換再儲存。
如果是簡單的資料格式轉換的話,可以在 Model 中用 $casts 定義。
這邊用 Todo 簡單做示範,用 $casts 定義將 use_id 的值轉換為布林值。
@@ -10,10 +10,22 @@ use App\Models\Tag;
class Todo extends Model
{
use HasFactory;
+
+ protected $casts = [
+ 'use_id' => 'boolean',
+ ];
試著讀出來看看
可以看到本來是數字的 user_id 都變成 true 了。
casting 只會影響讀取的值,當寫入資料時還是依資料表的欄位型別儲存。
casting 中比較常用到的是日期的轉換,把 UTC 格式轉成比較好視讀的樣子
protected $casts = [
'created_at' => 'datetime:Y-m-d',
];
如果不想把日期欄位一個個加到 casting ,可以定義一個共用的 serializeDate 轉換函式
@@ -6,14 +6,30 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Tag;
+use DateTimeInterface;
class Todo extends Model
{
use HasFactory;
protected $fillable = [
'name',
];
+
+ protected function serializeDate(DateTimeInterface $date)
+ {
+ return $date->format('Y-m-d');
+ }
如果要做比較複雜的轉換,讀取資料可以經由 Accessor 做資料變換,以 Todo 為例,可以看到目前的資料全都是小寫。
這時候在 Todo Model 中加上 Accessor getNameAttribute
/app/Models/Todo.php
@@ -14,6 +14,11 @@ class Todo extends Model
protected $fillable = [
'name',
];
+
+ public function getNameAttribute($value)
+ {
+ return strtoupper($value);
+ }
然後重新整理資料
資料庫中的資料完全沒變,讀出來後變成全大寫了。
因為定義了 getNameAttribute ,當經由 Todo Model 讀取資料時就會對每個 name 欄位的資料執行轉換,像這邊是用 strtoupper 將資料轉為全大寫。
要讓 Accessor 對應到欄位是經由函式的命名來設定
get<欄位的駝峰型>Attribute
舉例來說
name -> getNameAttribute
first_name -> getFirstNameAttribute
這種參照法還有個有趣的用途,就是創造原本不存在的欄位。
像是在 Model 建立 getUpperNameAttribute 函式
public function getUpperNameAttribute()
{
return strtoupper($this->name);
}
getUpperNameAttribute 會將目前 Model 中的 name 值轉為全大寫後回傳。
這時候用
Todo::first()->upper_name
會發現竟然能讀到值,明明 Todo 沒有 upper_name 這個欄位。
不過只是這樣定義的話,就只有動態讀取的時候能夠取得 upper_name 的值,並不會常駐在資料結構中,像是用 Todo::get() 的話會發現沒有 upper_name 。
如果希望讀取出來的每個 Todo 都帶有 upper_name 資料的話,要給 Model 追加定義 $appends 變數。
@@ -14,6 +14,13 @@ class Todo extends Model
protected $fillable = [
'name',
];
+ protected $appends = ['upper_name'];
public function getUpperNameAttribute()
{
return strtoupper($this->name);
}
Model 會根據 $appends 中的值尋找對應的 Accessor 讀取資料後,加入到現有的 Model 回傳當中。
注意只要有定義 $appends 就要為每個 append 的值定義 Accessor,不然找不到的話會直接報錯。
資料讀出來會先經過 casts 做轉換,才根據 appends 追加欄位,這造成兩種狀況
像這邊先把 name 轉換成布林值後 upper_name 也受影響了
上面講的都是讀取資料的轉換,Model 也可以定義在寫入資料時的轉換
@@ -6,14 +6,30 @@ class Todo extends Model
protected $fillable = [
'name',
];
+
+ public function setNameAttribute($value)
+ {
+ $this->attributes['name'] = strtolower($value);
+ }
一樣要注意函式的命名
set<欄位的駝峰型>Attribute
另外 Mutator 不像 Accessor 可以直接回傳值,必須賦值給 Model 上對應的欄位。