何谓 高阶消息传递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'
collect($arr)->sortBy(function($value) use ($age) {
return $value['age']
})
// 这样就清晰明了了。
复制代码
__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
就是很好的例子,上面有解析。