Laravel 源码学习之 Collection

本系列文章,主要基于在查看社区文档时,可能因为看到某句话,一时兴起,想要一探究竟,如何实现,查看源码所得,也仅限于对源码的分析和学习,巩固知识,希望2019年持续学习,扎实基础。
将持续更新。

概述:

Illuminate\Database\Eloquent\Collection继承了Laravel的集合基类Illuminate\Support\Collection。 大多数集合方法会返回新的 Eloquent 集合实例, 但是 pluck, keys, zip, collapse, flatten 和 flip 方法除外,它们会返回一个 集合基类 实例。同样,如果 map 操作返回的集合不包含任何 Eloquent 模型,那么它会被自动转换成集合基类。

2019/1/19 第二更之闲言碎语

通过这两天阅读源码,最深的感受就是,对这个框架越来越了解,不是说仅仅于代码上,是在看一个新的部分的时候,能很快的举一反三联想到它大概是如何实现,然后去源码中验证,希望这种感觉会越来越好,2019,刻意学习,每日精进。

2019/1/18 第一更

  • 关于Eloquent\Collection返回集合基类

    // Support\Collection::toBase()
    public function toBase()
    {
        return new self($this);
    }

    map操作中会在执行完基类 map() 方法后,用后期动态绑定仍然返回一个Eloquent\Collection实例,然后调用contains()方法:

        public function map(callable $callback)
        {
            $result = parent::map($callback);
    
            return $result->contains(function ($item) {
                return ! $item instanceof Model;
            }) ? $result->toBase() : $result;
        }
    
        public function contains($key, $operator = null, $value = null)
        {
            if (func_num_args() === 1) {
                if ($this->useAsCallable($key)) {
                    $placeholder = new stdClass;
    
                    return $this->first($key, $placeholder) !== $placeholder;
                }
                return in_array($key, $this->items);
            }
    
            return $this->contains($this->operatorForWhere(...func_get_args()));
        }

    contains()方法声明是传入键值对和操作符,但是它同时也支持仅传入一个值得情况,若 $key 不为回调,则仅用in_array判断。若 $key 为回调,传入 callback 和 stdClass instance作为默认值给first(),遍历集合的$items,判断! item instanceof Modelfirst()返回非 Model 类型的值或者默认的 $instance,通过与默认值比较,得出在map()操作后得到的集合中,是转为基类,还是返回$this,以便后续链式调用。

  • 关于Support\Collectionsum()方法
    看到sum()方法属于偶然,源于这样一行代码:

    $average = collect([
        ['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]
    ])->avg('foo');// 20

    比较好奇,它是如何传入key去这样的一维数组中得到平均数。于是开始看源码:

    public function avg($callback = null)
    {
        if ($count = $this->count()) {
            return $this->sum($callback) / $count;
        }
    }

    那么一般都知道,平均数是由 sum 得来的,,,所以,调用了基类的sum()方法。

    public function sum($callback = null)
    {
        if (is_null($callback)) {
            return array_sum($this->items);
        }
    
        $callback = $this->valueRetriever($callback);
    
        return $this->reduce(function ($result, $item) use ($callback) {
            return $result + $callback($item);
        }, 0);
    }

    那么 Laravel 中就是使用了非常多的 $callback 参数去适应各种输入,以达到灵活参数的目的,这就好比是 java 中的方法重载一样,只不过 php 是弱语言,对于参数类型没有严格限制而已。在这个方法中,如果不传参数,则类似于array_sum([1,2,3]);//6
    函数的返回值中写的很妙,在于将集合$items数组属性的每一项扔进一个回调中得到想要的值并参与计算总和,那么这个回调从哪儿来呢?从传入的参数中获得,因此,调用基类的valueRetriever($callback),该方法返回一个匿名函数:

    protected function valueRetriever($value)
    {
        if ($this->useAsCallable($value)) {
            return $value;
        }
    
        return function ($item) use ($value) {
            return data_get($item, $value);
        };
    }

    如果参数非函数,则造一个匿名函数,接收一个一维数组或者对象,返回键为 $value 的数组值或者对象的 $value 属性给上层调用去参与计算 sum。嗯,就是酱紫,总结就是:可传入参数有null,回调,或者指定值。而集合的$items[]通常有什么呢:基本类型值,数组(可多维,data_get()函数使用点语法),对象。

本作品采用《CC 协议》,转载必须注明作者和本文链接
坚持学习
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!