# 前言
學習一個框架, Ray 的想法是, 在深入理解底層實作的原理之前, 應該先知道這個框架的 使用方法
; 先學習怎麼使用這個前人造的輪子, 再學習怎麼樣造一個輪子。
所以本篇文章重點在於細讀官方文件, 並將內容理解後以 Q&A 的方式記錄下來, 加速學習以及查詢。
# Accessors & Mutators
# Defining An Accessor
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
} - Answer:
定義一個 accessor
當我使用 Eloquent Model 取得 first_name attribute 時,將 string 的第一個字符轉為大寫, 所以完成定義後, 當我使用$user->first_name;
, 會自動將原本的 value 第一個字符轉為大寫
以下的 Laravel example code 的意思是?
- Example:
<?php
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
} - Answer:
定義一個 accessor
full_name 是一個 computed value, 來源來自 first_name 以及 last_name column, 所以當我使用$user->full_name
時, 會自動取得 accessor 中定義的值
由於 full_name 並不存在於 table 中, 因此當我回傳整個 model 時並不會出現 full_name 這個 attribute, 可透過 append() 達成
# Defining A Mutator
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
} - Answer:
定義一個 mutator
當我們定義一個 model 的 attribute 時, mutator 會自動地呼叫 mutator, 對 attribute 進行操控
以此 example 來說, 如果我使用$user->first_name = 'Sally';
, 那就會自動將 Sally 轉成小寫, 這實在 Eloquent Model 中 first_name 會是 sally
# Date Mutators
Laravel 中, 預設 Eloquent 會將 created_at
以及 updated_at
轉成哪一個 class 的 instance?
Carbon
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $dates = [
'seen_at',
];
} - Answer:
定義一個 date mutator, 會將 seen_at column 預設轉換為 Carbon instance
以下的 Laravel example code 的意思是?
- Example:
<?php
$user = App\Models\User::find(1);
$user->deleted_at = now();
$user->save(); - Answer:
將 deleted_at 的時間變為現在時間, 預設時區使用 Laravel 時區
以下的 Laravel example code 的意思是?
- Example:
<?php
$user = App\Models\User::find(1);
return $user->deleted_at->getTimestamp(); - Answer:
將 deleted_at 使用 Carbon class 的 getTimestamp() 轉成 timestamp 格式
# Data Formats
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
protected $dateFormat = 'U';
} - Answer:
Laravel 預設的 timestamps 格式為'Y-m-d H:i:s
, 這也是會存在資料庫中的格式, 如果要自定義的話, 可以再 model 中的$dateFormat
property 自定義
# Attribute Casting
Laravel Model Attribute Casting 支援哪些 type?
- integer
- real
- float
- double
- decimal:
- string
- boolean
- object
- array
- collection
- date
- datetime
- timestamp
- encrypted
- encrypted:object
- encrypted:array
- encrypted:collection
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'is_admin' => 'boolean',
];
} - Answer:
假如資料庫存的是 0,1, $cast method 會在取得資料後自動將其轉成 boolean
Laravel Model Attribute Casting 可以 cast null 嗎?
不可
# Custom Casts
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class Json implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return json_decode($value, true);
}
public function set($model, $key, $value, $attributes)
{
return json_encode($value);
}
} - Answer:
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
// 定義一個 custom cast
class Json implements CastsAttributes
{
// get 代表如何將資料庫中取得的資料進行轉換
public function get($model, $key, $value, $attributes)
{
// 將資料庫取得的資料從 json 轉成 associatibe array
return json_decode($value, true);
}
// set 代表如何將資料轉換成適合存進資料庫的形式
public function set($model, $key, $value, $attributes)
{
// 將要存進資料庫的資料轉成 json 格式
return json_encode($value);
}
}
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use App\Casts\Json;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'options' => Json::class,
];
} - Answer:
在定義了 custom cast class 之後, 使用 User model 的 $cast property 來使用這個 custom cast class
# Value Object Casting
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Casts;
use App\Models\Address as AddressModel;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use InvalidArgumentException;
class Address implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return new AddressModel(
$attributes['address_line_one'],
$attributes['address_line_two']
);
}
public function set($model, $key, $value, $attributes)
{
if (! $value instanceof AddressModel) {
throw new InvalidArgumentException('The given value is not an Address instance.');
}
return [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
];
}
} - Answer:
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use InvalidArgumentException;
// 此為 value object casting
// 比方說, 我們在 User model 的 $cast property 定義 ['address' => App\Casts\Address], 再到 App\Models 下建立一個 Address model
// App\Models\Address class 為我們實際上要操作的 value object, 而當前這個 class 是這個 value object 與 User model 之間的 caster
// 當我使用 $user->address 時, 實際上便會呼叫當前 class 的 get(), 從 DB 中將資料帶入 App\Models\Address 這個 class 中
// 而當使用 $user->address->lineOne = '123', $user->save() 時, 則會呼叫 set(), 將 object 中的 property insert 到資料庫中的欄位
class Address implements CastsAttributes
{
// $model 代表呼叫 Address 的 model, $user->address, model 就是 $user
// $key 代表 address
// $value 代表 address 的 value
// $attribute 代表 raw data, 即 DB 中的 column
public function get($model, $key, $value, $attributes)
{
// 從 DB 中取出 'address_line_one', 'address_line_two', 並 assign 到 Address object 中的 lineOne, 及 lineTwo property
// 若 property 數量大於 attribute, 則會噴錯
return new Address(
$attributes['address_line_one'],
$attributes['address_line_two']
);
}
public function set($model, $key, $value, $attributes)
{
if (! $value instanceof Address) {
throw new InvalidArgumentException('The given value is not an Address instance.');
}
// 將 $value->lineOne 以及 lineTwo 儲存到資料庫的 address_line_one, address_line_two column
return [
'address_line_one' => $value->lineOne,
'address_line_two' => $value->lineTwo,
];
}
}
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Casts;
use App\Models\Address as AddressModel;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use InvalidArgumentException;
class Address implements CastsAttributes, SerializesCastableAttributes;
{
public function serialize($model, string $key, $value, array $attributes)
{
return (string) $value;
}
} - Answer:
short: 讓 custom cast class 可以 serialize object 或 JSON
long: 當使用 toArray method 來將 Eloquent model 轉成 array 或 JSON 時, custom cast value objects 將會被 serialize 如果有 implement Arrayable 或 JsonSerializable interface 的話。 然而, 當使用第三方 library 提供的 value objects 時, 可能無法在這些 object 上 implement 這些 interface, 因此可在 custom cast class implement SerializesCastableAttributes interface, 並新增 serialize method
# Inbound Casting
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
class Hash implements CastsInboundAttributes
{
protected $algorithm;
public function __construct($algorithm = null)
{
$this->algorithm = $algorithm;
}
public function set($model, $key, $value, $attributes)
{
return is_null($this->algorithm)
? bcrypt($value)
: hash($this->algorithm, $value);
}
} - Answer:
如果 custom cast 的作用範圍只限定於將要存進資料庫的資料作轉換, 但從資料庫取出資料時並不另外轉換, 在這樣的情況話, 就可以使用 inbound custom cast
需 implement CastsInboundAttributes interface, 以及只需 set method 即可
# Cast Parameters
以下的 Laravel example code 的意思是?
- Example:
<?php
class User extends Authenticatable;
{
protected $casts = [
'secret' => Hash::class.':sha256',
];
} - Answer:
將 parameter “sha256” 帶入 custom cast class “Hash”
# Castables
以下的 Laravel example code 的意思是?
- Example:
<?php
class User extends Authenticatable;
{
protected $casts = [
'address' => \App\Models\Address::class.':argument',
];
}
class Address implements Castable
{
public static function castUsing(array $arguments)
{
return AddressCast::class;
}
} - Answer:
<?php
// 這是實作 cast value object 的另外一種方式, 個人覺得這種方式比較直觀
class User extends Authenticatable;
{
// 除了指定為 custom cast class 之外, 也可以直接指定要操作的 cast value object class
// 需注意的是, 後面的 argument 會被 pass 到 custom cast class, 而不是 cast value object class
protected $casts = [
'address' => \App\Models\Address::class.':argument',
];
}
// 而在 cast value object class 中, 要 implement Castable interface
class Address implements Castable
{
// 並且, 要定義一個 castUsing method, 指定使用哪一個 custom cast class
public static function castUsing(array $arguments)
{
return AddressCast::class;
}
}
# Array & JSON Casting
以下的 Laravel example code 的意思是?
- Example:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $casts = [
'options' => 'array',
];
}
$user = App\Models\User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save(); - Answer:
如果 DB 內的內容是 JSON 或 TEXT 的話, 可以使用 array type, 當從資料庫取出資料時, 會自動轉成 PHP array, 而將 array 存進資料庫時, 則會自動轉成 JSON
以下的 Laravel example code 的意思是?
- Example:
<?php
$user = App\Models\User::find(1);
$user->update(['options->testKey' => 'testValue']); - Answer:
更新 JSON column 中的 key 為 ‘testKey’ 的 value 更新為 ‘testValue’
# Date Casting
以下的 Laravel example code 的意思是?
- Example:
<?php
protected $casts = [
'created_at' => 'datetime:Y-m-d',
]; - Answer:
雖說 created_at 預設就會被 cast 成 datetime type, 但還可以特別指定格式
當 model 被 serialized 成 array 或 JSON 時, 會使用這個格式
# Query Time Casting
以下的 Laravel example code 的意思是?
- Example:
<?php
$users = User::select([
'users.*',
'last_posted_at' => Post::selectRaw('MAX(created_at)')
->whereColumn('user_id', 'users.id')
])->withCasts([
'last_posted_at' => 'datetime'
])->get(); - Answer:
在 query 的過程中, 將 ‘last_posted_at’ 轉成 datetime type, 即 carbon instance, 若不使用 withCasts, 則 last_posted_at 會是一個 raw string, 如下 example<?php
$users = User::select([
'users.*',
'last_posted_at' => Post::selectRaw('MAX(created_at)')
->whereColumn('user_id', 'users.id')
])->withCasts([
'last_posted_at' => 'datetime'
])->get();
$last_posted_at = $users->first()->last_posted_at;
// 若不使用 cast, 是無法使用 Carbon class year property 的
dd($last_posted_at->year);
留言