Eloquent:更動子與型別轉換
簡介
通過存取子 (Accessor)、更動子 (Mutator)、與型別轉換,便可在從 Model 實體上存取 Eloquent 屬性時轉換其值。舉例來說,我們可能想用 Laravel 的加密功能 來在資料庫內加密某個值,並在從 Eloquent Model 上存取該屬性時自動解密。或者,我們可能會想將某個值轉換為 JSON 字串來儲存進資料庫,然後在 Eloquent Model 上以陣列來存取。
存取子 (Accessor) 與更動子 (Mutator)
定義存取子
存取子 (Accessor) 可在存取 Eloquent 屬性時轉換其值。若要定義存取子,可在 Model 中建立一個 get{Attribute}Attribute
方法。其中,{Attribute}
方法即為要存取的欄位之「Studly 命名法」大小寫的屬性名稱。
在這個例子中,我們會為 first_name
屬性定義存取子。在嘗試取得 first_name
屬性時,Eloquent 會自動呼叫該存取子:
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * Get the user's first name.11 *12 * @param string $value13 * @return string14 */15 public function getFirstNameAttribute($value)16 {17 return ucfirst($value);18 }19}
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * Get the user's first name.11 *12 * @param string $value13 * @return string14 */15 public function getFirstNameAttribute($value)16 {17 return ucfirst($value);18 }19}
如上所見,該欄位的原始值會傳給該存取子,讓你可以進行操作並回傳值。若要存取存取子的值,只需要在 Model 實體上存取 first_name
屬性即可:
1use App\Models\User;23$user = User::find(1);45$firstName = $user->first_name;
1use App\Models\User;23$user = User::find(1);45$firstName = $user->first_name;
存取子也不一定得要與單一屬性互動。你也可以使用存取子來回傳一個從現有屬性組合來的新的值:
1/**2 * Get the user's full name.3 *4 * @return string5 */6public function getFullNameAttribute()7{8 return "{$this->first_name} {$this->last_name}";9}
1/**2 * Get the user's full name.3 *4 * @return string5 */6public function getFullNameAttribute()7{8 return "{$this->first_name} {$this->last_name}";9}
若想讓過這些計算過的值包含在 Model 的陣列或 JSON 呈現上,則需要將這些欄位附加上去。
定義更動子
更動子 (Mutator) 可在寫入 Eloquent 屬性時轉換其值。若要定義更動子,可在 Model 中建立一個 set{Attribute}Attribute
方法。其中,{Attribute}
方法即為要存取的欄位之「Studly 命名法」大小寫的屬性名稱。
我們來為 first_name
屬性定義更動子。該更動子會在嘗試於 Model 上設定 first_name
屬性值時被呼叫:
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * Set the user's first name.11 *12 * @param string $value13 * @return void14 */15 public function setFirstNameAttribute($value)16 {17 $this->attributes['first_name'] = strtolower($value);18 }19}
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * Set the user's first name.11 *12 * @param string $value13 * @return void14 */15 public function setFirstNameAttribute($value)16 {17 $this->attributes['first_name'] = strtolower($value);18 }19}
該更動子會接收目前正在設定的屬性的值,讓你可以更改其值並將更改過的值設定在 Eloquent Model 內部的 $attributes
屬性上。若要使用這個更動子,只需要在 Eloquent Model 上設定 first_name
屬性即可:
1use App\Models\User;23$user = User::find(1);45$user->first_name = 'Sally';
1use App\Models\User;23$user = User::find(1);45$user->first_name = 'Sally';
在此範例中,setFirstNameAttribute
函式會以 Sally
值呼叫。改更動子接著會在名字上套用 strtolower
韓式,並將其結果設定到內部的 $attribuets
陣列上。
屬性型別轉換
屬性型別轉換提供了與存取子及更動子類似的方法。不過,你不需要手動在 Model 內定義任何額外的方法。通過 Model 上的 $casts
屬性,就可以方便地將屬性轉換為常見的資料型別。
$casts
屬性應為一個陣列,其索引鍵為要進行型別轉換的屬性名稱,而值則為要將該欄位進行型別轉換的型別。支援的轉換型別如下:
-
array
-
AsStringable::class
-
boolean
-
collection
-
date
-
datetime
-
immutable_date
-
immutable_datetime
-
decimal:
-
double
-
encrypted
-
encrypted:array
-
encrypted:collection
-
encrypted:object
-
float
-
integer
-
object
-
real
-
string
-
timestamp
為了演示屬性型別轉換,我們來對 is_admin
屬性進行型別轉換。該欄位在資料庫中是以整數 (0
或 1
) 來表示布林值的:
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * The attributes that should be cast.11 *12 * @var array13 */14 protected $casts = [15 'is_admin' => 'boolean',16 ];17}
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * The attributes that should be cast.11 *12 * @var array13 */14 protected $casts = [15 'is_admin' => 'boolean',16 ];17}
定義好型別轉換後,只要存取 is_admin
屬性,即使該屬性在資料庫中以整數來儲存,該屬性值總是會被轉換為布林值:
1$user = App\Models\User::find(1);23if ($user->is_admin) {4 //5}
1$user = App\Models\User::find(1);23if ($user->is_admin) {4 //5}
若有需要在執行階段加上新的、臨時的型別轉換,則可使用 mergeCasts
方法。這些型別轉換定義會被加到所有在 Model 中已定義的型別轉換上:
1$user->mergeCasts([2 'is_admin' => 'integer',3 'options' => 'object',4]);
1$user->mergeCasts([2 'is_admin' => 'integer',3 'options' => 'object',4]);
null
的屬性將不會進行型別轉換。此外,定義型別轉換 (或屬性) 時,也不應有相同名稱的關聯。
Stringable 的型別轉換
可以使用 Illuminate\Database\Eloquent\Casts\AsStringable
型別轉換類別來講 Model 屬性轉換為 [Fluent Illuminate\Support\Stringable
物件] (/docs/8.x/helpers#fluent-strings-method-list):
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Casts\AsStringable;6use Illuminate\Database\Eloquent\Model;78class User extends Model9{10 /**11 * The attributes that should be cast.12 *13 * @var array14 */15 protected $casts = [16 'directory' => AsStringable::class,17 ];18}
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Casts\AsStringable;6use Illuminate\Database\Eloquent\Model;78class User extends Model9{10 /**11 * The attributes that should be cast.12 *13 * @var array14 */15 protected $casts = [16 'directory' => AsStringable::class,17 ];18}
陣列與 JSON 的型別轉換
array
型別轉換特別適合搭配宜 JSON 序列化保存的欄位。舉例來說,說資料庫內有個包含了序列化 JSON 的 JSON
或 TEXT
欄位型別,則加上 array
型別轉換,就可以在從 Eloquent Model 上存取該欄位時自動將屬性反串聯化為 PHP 陣列:
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * The attributes that should be cast.11 *12 * @var array13 */14 protected $casts = [15 'options' => 'array',16 ];17}
1<?php23namespace App\Models;45use Illuminate\Database\Eloquent\Model;67class User extends Model8{9 /**10 * The attributes that should be cast.11 *12 * @var array13 */14 protected $casts = [15 'options' => 'array',16 ];17}
定義好型別轉換後,存取 options
屬性時就會自動從 JSON 反序列化為 PHP 陣列。為 options
賦值時,提供的陣列也會被序列化回 JSON 以進行儲存:
1use App\Models\User;23$user = User::find(1);45$options = $user->options;67$options['key'] = 'value';89$user->options = $options;1011$user->save();
1use App\Models\User;23$user = User::find(1);45$options = $user->options;67$options['key'] = 'value';89$user->options = $options;1011$user->save();
若要使用更精簡的方法來更新 JSON 屬性中的單一欄位,可以在呼叫 update
方法時使用 ->
運算子:
1$user = User::find(1);23$user->update(['options->key' => 'value']);
1$user = User::find(1);23$user->update(['options->key' => 'value']);
陣列物件與 Collection 的型別轉換
雖然使用標準的 array
型別轉換對於大多數專案就夠用了,但 array
也有一些缺點。由於 array
型別轉換回傳的是原生型別,因此我們無法直接更改陣列的元素。舉例來說,下列程式碼會觸發 PHP 錯誤:
1$user = User::find(1);23$user->options['key'] = $value;
1$user = User::find(1);23$user->options['key'] = $value;
為了解決這個問題,Laravel 提供了一個 AsArrayObject
型別轉換,可用來將 JSON 屬性轉換為 ArrayObject 類別。改功能使用 Laravel 的自訂型別轉換實作,可讓 Laravel 進行智慧快取並變換更改過的物件,也能讓個別元素在修改時不觸發 PHP 錯誤。若要使用 AsArrayObject
型別轉換,只需要將其指派給屬性即可:
1use Illuminate\Database\Eloquent\Casts\AsArrayObject;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'options' => AsArrayObject::class,10];
1use Illuminate\Database\Eloquent\Casts\AsArrayObject;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'options' => AsArrayObject::class,10];
Laravel 還提供了一個類似的 AsCollection
型別轉換,可將 JSON 屬性轉換為 Laravel 的 Collection 實體:
1use Illuminate\Database\Eloquent\Casts\AsCollection;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'options' => AsCollection::class,10];
1use Illuminate\Database\Eloquent\Casts\AsCollection;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'options' => AsCollection::class,10];
日期的型別轉換
預設情況下,Eloquent 會將 created_at
與 updated_at
欄位轉換為 Carbon 實體。Carbon 繼承自 PHP 的 DateTime
類別,並提供了各種實用方法。可以通過往 Model 的 $casts
屬性陣列內定義額外的日期型別轉換來給其他日期屬性進行轉換。通常來說,日期應使用 datetime
或 immutable_datetime
型別轉換類型。
在定義 date
或 datetime
型別轉換時,也可以指定日期的格式。該格式會在 Model 被序列化成陣列或 JSON 時使用:
1/**2 * The attributes that should be cast.3 *4 * @var array5 */6protected $casts = [7 'created_at' => 'datetime:Y-m-d',8];
1/**2 * The attributes that should be cast.3 *4 * @var array5 */6protected $casts = [7 'created_at' => 'datetime:Y-m-d',8];
在將欄位轉換為日期時,可以將相應的 Model 屬性值設為 UNIX 時戳、日期字串 (Y-m-d
)、日期與時間字串、或是 DateTime
/ Carbon
實體。日期的值會被正確地轉換並保存在資料庫中。
在 Model 中定義 serializeDate
方法,即可為 Model 中所有的日期定義預設的序列化方法。改方法並不會影響日期儲存到資料庫時的格式化方法:
1/**2 * Prepare a date for array / JSON serialization.3 *4 * @param \DateTimeInterface $date5 * @return string6 */7protected function serializeDate(DateTimeInterface $date)8{9 return $date->format('Y-m-d');10}
1/**2 * Prepare a date for array / JSON serialization.3 *4 * @param \DateTimeInterface $date5 * @return string6 */7protected function serializeDate(DateTimeInterface $date)8{9 return $date->format('Y-m-d');10}
若要指定用來將 Model 日期保存在資料庫時使用的格式,可在 Model 中定義 $dateFormat
屬性:
1/**2 * The storage format of the model's date columns.3 *4 * @var string5 */6protected $dateFormat = 'U';
1/**2 * The storage format of the model's date columns.3 *4 * @var string5 */6protected $dateFormat = 'U';
日期型別轉換、序列化、與時區
預設情況下,不論專案的 timezone
設定選項設為哪個時區,date
與 datetime
都會將日期序列化為 UTC 的 ISO-8601 日期字串 (1986-05-28T21:05:54.000000Z
)。我們強烈建議你保持使用這個序列化格式,也建議你只將專案的 timezone
設定選項設為預設的 UTC
,並讓專案中以 UTC 來儲存所有的日期時間。在專案中保持一致地使用 UTC 時區,可為其他 PHP 與 JavaScript 的日期操作函示庫提供最大的互用性。
若有在 date
或 datetime
型別轉換內提供自訂格式,如 datetime:Y-m-d H:i:s
,則在進行日期序列化時,會使用 Carbon 實體內部的時區。一般來說,這個時區就是專案的 timezone
設定選項。
Enum 的型別轉換
Enum 型別轉換只可在 PHP 8.1 以上使用。
Eloquent 也能讓我們將屬性值轉換為 PHP Enum。為此,可在 Model 中的 $casts
屬性陣列中指定要型別轉換的屬性與 Enum:
1use App\Enums\ServerStatus;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'status' => ServerStatus::class,10];
1use App\Enums\ServerStatus;23/**4 * The attributes that should be cast.5 *6 * @var array7 */8protected $casts = [9 'status' => ServerStatus::class,10];
定義好 Model 的型別轉換後,每次存取該屬性時就會自動轉換對 Enum 進行轉換:
1if ($server->status == ServerStatus::provisioned) {2 $server->status = ServerStatus::ready;34 $server->save();5}
1if ($server->status == ServerStatus::provisioned) {2 $server->status = ServerStatus::ready;34 $server->save();5}
加密的型別轉換
encrypted
型別轉換會通過 Laravel 的內建加密功能來加密 Model 的屬性值。此外,還有 encrypted:array
, encrypted:collection
, encrypted:object
, AsEncryptedArrayObject
, 與 AsEncryptedCollection
等型別轉換,這些型別轉換都與其未加密的版本一樣以相同方式運作。不過,可想而知,底層的值會先加密才保存進資料庫。
由於加密後的文字長度時無法預測的,且通常比明文的版本還要長,因此請確保其資料庫欄位為 TEXT
型別或更大的型別。此外,由於在資料庫中值都是經過加密的,因此你也沒辦法查詢或搜尋加密過的屬性質。
查詢時的型別轉換
有時候我們可能會需要在執行查詢時套用型別轉換,例如從資料表中選擇原始資料時。舉例來說,假設有下列查詢:
1use App\Models\Post;2use App\Models\User;34$users = User::select([5 'users.*',6 'last_posted_at' => Post::selectRaw('MAX(created_at)')7 ->whereColumn('user_id', 'users.id')8])->get();
1use App\Models\Post;2use App\Models\User;34$users = User::select([5 'users.*',6 'last_posted_at' => Post::selectRaw('MAX(created_at)')7 ->whereColumn('user_id', 'users.id')8])->get();
查詢結果中的 last_posted_at
屬性會是字串。如果我們可以將 datetime
型別轉換在執行查詢時套用到這個屬性上就好了。好佳在,我們可以通過使用 withCasts
方法來達成:
1$users = User::select([2 'users.*',3 'last_posted_at' => Post::selectRaw('MAX(created_at)')4 ->whereColumn('user_id', 'users.id')5])->withCasts([6 'last_posted_at' => 'datetime'7])->get();
1$users = User::select([2 'users.*',3 'last_posted_at' => Post::selectRaw('MAX(created_at)')4 ->whereColumn('user_id', 'users.id')5])->withCasts([6 'last_posted_at' => 'datetime'7])->get();
自訂型別轉換
Laravel 中有各式內建的實用型別轉換類型。不過,有時候我們也會需要定義自己的型別轉換類型。我們可以通過實作 CastsAttributes
介面來自訂型別轉換類型。
實作了這個介面的類別必須定義一組 get
與 set
方法。get
方法用於將儲存在資料庫內的原始值轉換為型別值;set
方法則負責將型別值轉換為可儲存在資料庫內的原始值。在這裡,我們將重新實作一個內建的 json
型別轉換類型為例:
1<?php23namespace App\Casts;45use Illuminate\Contracts\Database\Eloquent\CastsAttributes;67class Json implements CastsAttributes8{9 /**10 * Cast the given value.11 *12 * @param \Illuminate\Database\Eloquent\Model $model13 * @param string $key14 * @param mixed $value15 * @param array $attributes16 * @return array17 */18 public function get($model, $key, $value, $attributes)19 {20 return json_decode($value, true);21 }2223 /**24 * Prepare the given value for storage.25 *26 * @param \Illuminate\Database\Eloquent\Model $model27 * @param string $key28 * @param array $value29 * @param array $attributes30 * @return string31 */32 public function set($model, $key, $value, $attributes)33 {34 return json_encode($value);35 }36}
1<?php23namespace App\Casts;45use Illuminate\Contracts\Database\Eloquent\CastsAttributes;67class Json implements CastsAttributes8{9 /**10 * Cast the given value.11 *12 * @param \Illuminate\Database\Eloquent\Model $model13 * @param string $key14 * @param mixed $value15 * @param array $attributes16 * @return array17 */18 public function get($model, $key, $value, $attributes)19 {20 return json_decode($value, true);21 }2223 /**24 * Prepare the given value for storage.25 *26 * @param \Illuminate\Database\Eloquent\Model $model27 * @param string $key28 * @param array $value29 * @param array $attributes30 * @return string31 */32 public function set($model, $key, $value, $attributes)33 {34 return json_encode($value);35 }36}
定義好自訂的型別轉換類型後,就可以使用類別名稱將其附加到 Model 屬性內:
1<?php23namespace App\Models;45use App\Casts\Json;6use Illuminate\Database\Eloquent\Model;78class User extends Model9{10 /**11 * The attributes that should be cast.12 *13 * @var array14 */15 protected $casts = [16 'options' => Json::class,17 ];18}
1<?php23namespace App\Models;45use App\Casts\Json;6use Illuminate\Database\Eloquent\Model;78class User extends Model9{10 /**11 * The attributes that should be cast.12 *13 * @var array14 */15 protected $casts = [16 'options' => Json::class,17 ];18}
數值物件的型別轉換
進行型別轉換時,我們不只可以將值轉換為 PHP 的原生型別,我們還可以將值轉換為物件。定義這種將值轉換為物件的自訂型別轉換就跟轉換成原生型別類似。不過,在這種型別轉換類別中的 set
方法應回傳一組在 Model 上用於設定原始、可儲存值的索引鍵/值配對。
在這裡,我們以將多個 Model 值轉換到單一 Address
數值物件的自訂型別轉換類別為例。我們假設 Address
值有兩個公用屬性:lineOne
與 lineTwo
:
1<?php23namespace App\Casts;45use App\Models\Address as AddressModel;6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;7use InvalidArgumentException;89class Address implements CastsAttributes10{11 /**12 * Cast the given value.13 *14 * @param \Illuminate\Database\Eloquent\Model $model15 * @param string $key16 * @param mixed $value17 * @param array $attributes18 * @return \App\Models\Address19 */20 public function get($model, $key, $value, $attributes)21 {22 return new AddressModel(23 $attributes['address_line_one'],24 $attributes['address_line_two']25 );26 }2728 /**29 * Prepare the given value for storage.30 *31 * @param \Illuminate\Database\Eloquent\Model $model32 * @param string $key33 * @param \App\Models\Address $value34 * @param array $attributes35 * @return array36 */37 public function set($model, $key, $value, $attributes)38 {39 if (! $value instanceof AddressModel) {40 throw new InvalidArgumentException('The given value is not an Address instance.');41 }4243 return [44 'address_line_one' => $value->lineOne,45 'address_line_two' => $value->lineTwo,46 ];47 }48}
1<?php23namespace App\Casts;45use App\Models\Address as AddressModel;6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;7use InvalidArgumentException;89class Address implements CastsAttributes10{11 /**12 * Cast the given value.13 *14 * @param \Illuminate\Database\Eloquent\Model $model15 * @param string $key16 * @param mixed $value17 * @param array $attributes18 * @return \App\Models\Address19 */20 public function get($model, $key, $value, $attributes)21 {22 return new AddressModel(23 $attributes['address_line_one'],24 $attributes['address_line_two']25 );26 }2728 /**29 * Prepare the given value for storage.30 *31 * @param \Illuminate\Database\Eloquent\Model $model32 * @param string $key33 * @param \App\Models\Address $value34 * @param array $attributes35 * @return array36 */37 public function set($model, $key, $value, $attributes)38 {39 if (! $value instanceof AddressModel) {40 throw new InvalidArgumentException('The given value is not an Address instance.');41 }4243 return [44 'address_line_one' => $value->lineOne,45 'address_line_two' => $value->lineTwo,46 ];47 }48}
對數值物件進行型別轉換時,對數值物件進行的所有更改都會在 Model 儲存前同步回 Model 上:
1use App\Models\User;23$user = User::find(1);45$user->address->lineOne = 'Updated Address Value';67$user->save();
1use App\Models\User;23$user = User::find(1);45$user->address->lineOne = 'Updated Address Value';67$user->save();
若有打算要將包含數值物件的 Eloquent Model 序列化為 JSON 或陣列,則該數值物件應實作 Illuminate\Contracts\Support\Arrayable
與 JsonSerializable
介面。
Array / JSON 的序列化
當 Eloquent Model 通過 toArray
與 toJson
轉換為陣列或 JSON 時,只要自訂的型別轉換數值物件有實作 Illuminate\Contracts\Support\Arrayable
與 JsonSerializable
介面,該數值物件也會一併被序列化。不過,若我們使用的數值物件是來自第三方套件的,那我們可能就沒辦法提供這些負責序列化介面。
因此,我們可以指定讓自訂型別轉換類別來負責處理數值物件的序列化。為此,自訂型別轉換類別應實作 Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes
介面。實作這個介面,就代表該類別中應包含一個 serialize
方法,該方法應回傳數值物件的序列化形式:
1/**2 * Get the serialized representation of the value.3 *4 * @param \Illuminate\Database\Eloquent\Model $model5 * @param string $key6 * @param mixed $value7 * @param array $attributes8 * @return mixed9 */10public function serialize($model, string $key, $value, array $attributes)11{12 return (string) $value;13}
1/**2 * Get the serialized representation of the value.3 *4 * @param \Illuminate\Database\Eloquent\Model $model5 * @param string $key6 * @param mixed $value7 * @param array $attributes8 * @return mixed9 */10public function serialize($model, string $key, $value, array $attributes)11{12 return (string) $value;13}
輸入型別轉換
我們偶爾會需要一種型別轉換:只在對 Model 賦值時轉換值,存取時則不進行任何轉換。純輸入的型別轉換最常見的例子就是「雜湊」型別轉換。純輸入的型別轉換應實作 CastsInboundAttributes
介面,該介面只要求要定義 set
方法。
1<?php23namespace App\Casts;45use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;67class Hash implements CastsInboundAttributes8{9 /**10 * The hashing algorithm.11 *12 * @var string13 */14 protected $algorithm;1516 /**17 * Create a new cast class instance.18 *19 * @param string|null $algorithm20 * @return void21 */22 public function __construct($algorithm = null)23 {24 $this->algorithm = $algorithm;25 }2627 /**28 * Prepare the given value for storage.29 *30 * @param \Illuminate\Database\Eloquent\Model $model31 * @param string $key32 * @param array $value33 * @param array $attributes34 * @return string35 */36 public function set($model, $key, $value, $attributes)37 {38 return is_null($this->algorithm)39 ? bcrypt($value)40 : hash($this->algorithm, $value);41 }42}
1<?php23namespace App\Casts;45use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;67class Hash implements CastsInboundAttributes8{9 /**10 * The hashing algorithm.11 *12 * @var string13 */14 protected $algorithm;1516 /**17 * Create a new cast class instance.18 *19 * @param string|null $algorithm20 * @return void21 */22 public function __construct($algorithm = null)23 {24 $this->algorithm = $algorithm;25 }2627 /**28 * Prepare the given value for storage.29 *30 * @param \Illuminate\Database\Eloquent\Model $model31 * @param string $key32 * @param array $value33 * @param array $attributes34 * @return string35 */36 public function set($model, $key, $value, $attributes)37 {38 return is_null($this->algorithm)39 ? bcrypt($value)40 : hash($this->algorithm, $value);41 }42}
型別轉換的參數
在 Model 上設定自訂型別轉換時,可以指定型別轉換的參數,請使用 :
字元來區分型別轉換類別名稱與參數,並使用逗號來區分多個參數。這些參數會傳給型別轉換類別的建構函式:
1/**2 * The attributes that should be cast.3 *4 * @var array5 */6protected $casts = [7 'secret' => Hash::class.':sha256',8];
1/**2 * The attributes that should be cast.3 *4 * @var array5 */6protected $casts = [7 'secret' => Hash::class.':sha256',8];
Castable
我們可以讓專案中的數值物件自己定義自己的自訂型別轉換類別。與在 Model 中設定自訂的型別轉換類別相比,我們可以設定實作了 Illuminate\Contracts\Database\Eloquent\Castable
介面的數值物件類別:
1use App\Models\Address;23protected $casts = [4 'address' => Address::class,5];
1use App\Models\Address;23protected $casts = [4 'address' => Address::class,5];
實作了 Castable
介面的物件必須定義 castUsing
方法。該方法則應回傳用於對 Castable
類別進行型別轉換的自訂型別轉換類別名稱:
1<?php23namespace App\Models;45use Illuminate\Contracts\Database\Eloquent\Castable;6use App\Casts\Address as AddressCast;78class Address implements Castable9{10 /**11 * Get the name of the caster class to use when casting from / to this cast target.12 *13 * @param array $arguments14 * @return string15 */16 public static function castUsing(array $arguments)17 {18 return AddressCast::class;19 }20}
1<?php23namespace App\Models;45use Illuminate\Contracts\Database\Eloquent\Castable;6use App\Casts\Address as AddressCast;78class Address implements Castable9{10 /**11 * Get the name of the caster class to use when casting from / to this cast target.12 *13 * @param array $arguments14 * @return string15 */16 public static function castUsing(array $arguments)17 {18 return AddressCast::class;19 }20}
即使是使用 Castable
類別,也可以在 $casts
定義中提供引數。這些引數會被傳給 castUsing
方法:
1use App\Models\Address;23protected $casts = [4 'address' => Address::class.':argument',5];
1use App\Models\Address;23protected $casts = [4 'address' => Address::class.':argument',5];
Castable 與匿名型別轉換類別
通過將「Castable」與 PHP 的匿名函式搭配使用,我們就能在單一 Castable 物件內定義數值物件與其型別轉換邏輯。為此,請在數值物件的 castUsing
方法內回傳一個匿名類別。這個匿名類別應實作 CastsAttributes
介面:
1<?php23namespace App\Models;45use Illuminate\Contracts\Database\Eloquent\Castable;6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;78class Address implements Castable9{10 // ...1112 /**13 * Get the caster class to use when casting from / to this cast target.14 *15 * @param array $arguments16 * @return object|string17 */18 public static function castUsing(array $arguments)19 {20 return new class implements CastsAttributes21 {22 public function get($model, $key, $value, $attributes)23 {24 return new Address(25 $attributes['address_line_one'],26 $attributes['address_line_two']27 );28 }2930 public function set($model, $key, $value, $attributes)31 {32 return [33 'address_line_one' => $value->lineOne,34 'address_line_two' => $value->lineTwo,35 ];36 }37 };38 }39}
1<?php23namespace App\Models;45use Illuminate\Contracts\Database\Eloquent\Castable;6use Illuminate\Contracts\Database\Eloquent\CastsAttributes;78class Address implements Castable9{10 // ...1112 /**13 * Get the caster class to use when casting from / to this cast target.14 *15 * @param array $arguments16 * @return object|string17 */18 public static function castUsing(array $arguments)19 {20 return new class implements CastsAttributes21 {22 public function get($model, $key, $value, $attributes)23 {24 return new Address(25 $attributes['address_line_one'],26 $attributes['address_line_two']27 );28 }2930 public function set($model, $key, $value, $attributes)31 {32 return [33 'address_line_one' => $value->lineOne,34 'address_line_two' => $value->lineTwo,35 ];36 }37 };38 }39}