Laravel 集合的高阶消息传递

Laravel 5.4 新增了对于集合的「高阶消息传递」(Higher Order Messages,简写 HOM)的支持。

什么是「高阶消息传递」呢?

Image.jpg

计算用户的投票数总和:

$users = User::where('group', 'Development')->get()

// 传统写法
$users->sum('votes');

// HOM 写法
$users->sum->votes;

投票数大于 500 的用户标记成 VIP:

$users = User::where('votes', '>', 500)->get();

// 传统写法
$users->each(function ($user) {
    $user->markAsVip();
});

// HOM 写法
$users->each->markAsVip();

高阶消息传递允许消息有其它的消息作为参数。Laravel 集合的高阶消息传递就是一种语法糖,提供了对集合执行常用操作的快捷方式。

「高阶消息传递」实现原理

Illuminate\Support\Collection 中有如下代码:

/**
 * The methods that can be proxied.
 *
 * @var array
 */
protected static $proxies = [
    'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap',
    'keyBy', 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique',
];

...

/**
 * Dynamically access collection proxies.
 *
 * @param  string  $key
 * @return mixed
 *
 * @throws \Exception
 */
public function __get($key)
{
    if (! in_array($key, static::$proxies)) {
        throw new Exception("Property [{$key}] does not exist on this collection instance.");
    }

    return new HigherOrderCollectionProxy($this, $key);
}

当对集合调用 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap', 'keyBy', 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique' 这些属性时,会返回一个 HigherOrderCollectionProxy 的实例。

Illuminate\Support\HigherOrderCollectionProxy 中的源码:

/**
 * Proxy accessing an attribute onto the collection items.
 *
 * @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};
    });
}

/**
 * Proxy a method call onto the collection items.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 */
public function __call($method, $parameters)
{
    return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
        return $value->{$method}(...$parameters);
    });
}

HigherOrderCollectionProxy 实例调用不存在的属性时,会返回当前 HigherOrderCollectionProxy 实例代理的集合方法。该集合方法使用闭包作为参数,闭包返回集合中的成员的相应属性。

当调用不存在的方法时,也会返回代理的集合方法,也同样以闭包作为其参数。不同点只是闭包返回的是集合中的成员去调用相应的方法。

结合前面的例子应用到 HigherOrderCollectionProxy 源码中来理解:

// HOM 写法
$users->sum->votes;
// 将 $this->method 替换为 sum,$key 替换为 votes,HigherOrderCollectionProxy 中的 __get() 方法返回
$users->sum(function ($value) {
    return is_array($value) ? $value['votes'] : $value->{'votes'};
});

// HOM 写法
$users->each->markAsVip();
// 将 $this->method 替换为 each,$method 替换为 markAsVip,$parameters 替换成空数组,HigherOrderCollectionProxy 中的 __call() 方法返回
$this->collection->each(function ($value) {
    return $value->markAsVip([]);
});

「高阶消息传递」写法相比传统写法更加简单明了,对集合使用一些简单常用的操作时推荐使用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注