LoginSignup
6
4

More than 5 years have passed since last update.

LaravelのHigherOrderCollectionProxyの仕組み

Last updated at Posted at 2019-04-16

HigherOrderCollectionProxyとは

PHP
$collection = collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map(function ($collection) {
    return $collection->average();
});

$collection = collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map->average();

上を下のようにメソッドチェーンで繋げられるようにしたものです。

collect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])->map__get()メソッドを呼びHigherOrderCollectionProxyオブジェクトを返します。

/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php
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);
}

phpではクラスに存在しないプロパティ(ここではmap)を読み込むとマジックメソッド__get()を呼び出します。
$keyはプロはティ名の文字列です。
そしてstatic::$proxiesの中にmapが入っているかをチェックしてから、HigherOrderCollectionProxyを返します。
第一引数にはcollect([collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])])
第二引数には'map'が入ります。

/laravel/framework/src/Illuminate/Support/Collection.php
/**
 * The methods that can be proxied.
 *
 * @var array
 */
protected static $proxies = [
    'average', 'avg', 'contains', 'each', 'every', 'filter', 'first',
    'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition',
    'reject', 'some', 'sortBy', 'sortByDesc', 'sum', 'unique',
];
/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php
public function __construct(Collection $collection, $method)
{
    $this->method = $method;
    $this->collection = $collection;
}

$this->method'map'$this->collectionCollectionオブジェクトが入ります。

そして->average()を呼び出します。
HigherOrderCollectionProxyにはaverage()メソッドはないので、マジックメソッド__call()が呼ばれます。

/laravel/framework/src/Illuminate/Support/HigherOrderCollectionProxy.php
/**
 * 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);
    });
}

$methodはメソッド名の文字列(ここでは'average')、$parametersは引数を格納した入れ配列(average()は引数がないので空の配列)になります。

つまり__call()の中身は

PHP
return $this->collection->map(function ($value) use ('average', []) {
    return $value->average(...[]);
});

ということになります。

/laravel/framework/src/Illuminate/Support/Collection.php
/**
 * Run a map over each of the items.
 *
 * @param  callable  $callback
 * @return static
 */
public function map(callable $callback)
{
    $keys = array_keys($this->items);

    $items = array_map($callback, $this->items, $keys);

    return new static(array_combine($keys, $items));
}

$callbackfunction ($value) use ('average', []) { return $value->average(...[]); }
$this->items[collect([1, 2, 3, 4, 5]), collect([6, 7, 8, 9, 10])]
$keys[0, 1]です。

array_map() は、array1 の各要素に callback 関数を適用した後、 その全ての要素を含む配列を返します。

array_map()

つまり__call()$valueにはそれぞれ、collect([1, 2, 3, 4, 5]とcollect([6, 7, 8, 9, 10])が入ります。

そしてaverage()で平均が計算され

PHP
var_dump($collection->all());
/*
array(2) {
  [0]=>
  int(3)
  [1]=>
  int(8)
}
*/

となります。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4