何謂 高階訊息傳遞Higher Order Messages(HOM) 其實就是個叫法,我也不太清楚。比如
each
方法,就屬於集合的高階訊息的方法內,要求應該就是引數可傳一個閉包,可以這麼理解。
1. 示例
先簡單說說它怎麼個用法,官方文件是有的,拿過來介紹一下。
// 在某控制器的方法裡
$users = User::where('votes', '>', 500)->get();
$users->each->markAsVip();
// user模型裡
public function markAsVip()
{
// 這裡假設就是這麼更改,有is_vip這個欄位
$this->update('is_vip', 1);
}
複製程式碼
以上,就屬於高階用法的例項。將 user
表裡的 votes
數大於500的標記為vip會員。
如果是正常寫法,那鐵定就是,將 $users
進行 foreach
遍歷 然後進行逐條更新。相比集合高階用法。就不必多說了吧。
2. Collection 裡 涉及高階訊息原始碼解析
/**
* 可以被作為高階訊息傳遞的方法
*
* @var array
*/
protected static $proxies = [
'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap',
'keyBy', 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique',
];
/**
* 新增自定義高階方法
*
* @param string $method
* @return void
*/
public static function proxy($method)
{
static::$proxies[] = $method;
}
public function __get($key)
{
// 就是如果傳的 $key 在高階方法陣列裡都不存在,那麼報錯
if (! in_array($key, $static::$proxies)) {
throw new Exception("Property [{$key}] does not exist on this collection instance.")
}
// 否則, 將Collection例項和這個方法名以引數
// 傳給 HigherOrderCollectionProxy 例項化
return new HigherOrderCollectionProxy($this, $key);
}
複製程式碼
上面,就是定義了靜態變數,存放高階方法名,還有可自定義的方法,和利用魔術方法__get()
來跳轉到 HigherOrderCollectionProxy
類裡得以實現。
3.HigherOrderCollectionProxy 類解析
- 成員變數和構造方法
class HigherOrderCollectionProxy {
/**
* 儲存 collection類例項
*
* @var \Illuminate\Support\Collection
*/
protected $collection;
/**
* 儲存 高階方法名
*
* @var string
*/
protected $method;
/**
* 構造接收傳過來的 collection類例項, 高階方法名
*
* @param \Illuminate\Support\Collection $collection
* @param string $method
* @return void
*/
public function __construct(Collection $collection, $method)
{
// 簡單初始化賦值儲存
$this->method = $method;
$this->collection = $collection;
}
複製程式碼
__get
方法解析
/**
* 用來 針對屬性 高階傳遞呼叫
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
return $this->collection->{$this->method}(function ($value) use ($key) {
return is_array($value) ? $value[$key] : $value->{$key};
});
}
// 說明:為了某些集合方法的引數-閉包裡 傳屬性的東東
// 舉例: 將下列陣列按年齡正序排序
$arr = [
[
'name' => '小明',
'age' => 23,
'sex' => '男'
],
[
'name' => '小鑫',
'age' => 21,
'sex' => '女'
]
[
'name' => '小hu',
'age' => 22,
'sex' => '男'
]
];
collect($arr)->sortBy->age;
// 將__get 解析一下:
$age = 'age'
$datas = collect($arr)->sortBy(function($value) use ($age) {
return $value[$age];
})
$datas->values()->all();
/*
[
[
'name' => '小鑫',
'age' => 21,
'sex' => '女'
],
[
'name' => '小hu',
'age' => 22,
'sex' => '男'
],
[
'name' => '小明',
'age' => 23,
'sex' => '男'
],
]
*/
// 這樣就清晰明瞭了。
複製程式碼
__call()
方法解析
/**
* 針對方法的 高階訊息傳遞
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->collection->{$this->method}($value) use ($method, $parameters) {
return $value->{$method}(...$parameters);
}
}
// 說明: 針對高階集合呼叫方法,繼續舉例子
// 舉例:再把第一節的例子拿來
// 在某控制器的方法裡
$users = User::where('votes', '>', 500)->get();
$users->each->markAsVip();
// user模型裡
public function markAsVip()
{
// 這裡假設就是這麼更改,有is_vip這個欄位
$this->update('is_vip', 1);
}
// 按__call()方法解析後
$this->collection 就是 $users
$this->method 就是 'each'
$method 就是 'markAsVip'
$parameters 就是 空的
// 所以呢
$method = 'markAsVip'
$users->each(function($value) use ($method) {
return $value->$method();
})
// 這樣不就是用each將$users遍歷 然後呼叫User模型裡的markAsVip()方法麼
// 其實就是這樣 啊哈哈哈哈。
複製程式碼
4.總結
__get方法
:當用例項呼叫獲取屬性的時候,那麼會自動呼叫該類裡的__get
方法。 而高階訊息傳遞類HigherOrderCollectionProxy
就利用這個作為跳板,進一步處理。__call方法
:當用例項呼叫某個成員方法的時候,不存在,就會自動呼叫該類的__call
方法。 如集合的sum
和each
就是很好的例子,上面有解析。