Lavavel笔记 Eloquent ORM分页源码分析

安装了laravel-debugbar后打开一个列表页面,发现页面输出有两个 select count(*) 语句,这是一个严重的设计缺陷呀。

查看代码

$users = User::where(‘votes‘, ‘>‘, 100)->paginate(15);
$count = User::where(‘votes‘, ‘>‘, 100)->count();

之前就感觉paginate分页应该是使用了count,但是不知道怎么取总量数据所以又写了一个count()。

var_dump($users);
object(Illuminate\Pagination\LengthAwarePaginator)[306]  
  protected ‘total‘ => int 4289
  protected ‘lastPage‘ => int 143
  protected ‘items‘ => 
    object(Illuminate\Database\Eloquent\Collection)[307]      
    protected ‘items‘ => 
        array (size=30)
          0 => ....

返回数据里面 的确有 protected ‘total‘,但是protected不能访问呀!

仔细看了一下文档,$results->total(),原来取total需要的是方法,而不是属性。

弄明白total的获取以后,对paginate这个分页方法产生了兴趣。于是看了一下源码。

paginate这个方法最后使用了

Illuminate\Pagination\LengthAwarePaginator

这个类是怎么调用的呢?

无论是User::where(‘votes‘, ‘>‘, 100)->paginate(15)还是User::paginate(15),

User继承着Illuminate\Database\Eloquent\Model这个ORM类,但是在Model并没有where和paginate这些方法或静态方法,这是laravl使用的一种代码设计模式,

    /**
     * Handle dynamic static method calls into the method.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public static function __callStatic($method, $parameters)
    {
        $instance = new static;
        return call_user_func_array([$instance, $method], $parameters);
    }

__callStatic是php类的魔术方法

http://php.net/manual/zh/language.oop5.overloading.php#object.callstatic

 public static mixed __callStatic ( string $name , array $arguments )

在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。

laravel Model的__callStatic实现的业务

new static;

new当前model,也就是new User,这个是static静态延迟绑定的使用,可以和“new self;”使用进行比较。

call_user_func_array([$instance, $method], $parameters);

主要是这段,call_user_func_array 调用了new static类即User的where或paginate方法,传递 $parameters参数。

然后,然后,然后,User这个继承Model类里面仍然没有where或paginate方法,那么使用 Model的__call这魔术方法。

    /**
     * Handle dynamic method calls into the model.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (in_array($method, [‘increment‘, ‘decrement‘])) {
            return call_user_func_array([$this, $method], $parameters);
        }
        $query = $this->newQuery();
        return call_user_func_array([$query, $method], $parameters);
    }

开始还以为这么处理多了一层魔术方法,浪费效率,想想突然明白了,

User::where();
$user = new User; 
$user->paginate(15);

代码是一样的,只不过用__callStatic的 new static代替了“new User;”,

用call_user_func_array代替了调用函数和传参,lavarel的简洁可见一斑。

继续分析,如果调用的方法是 increment或者decrement,那么使用的是User类的方法。

否则使用 $this->newQuery();

newQuery这个方法的层级太深,没能理解,不过看注释,主要是调用

‘Illuminate\Database\Eloquent\Builder‘

    /**
     * Get a new query builder for the model‘s table.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function newQuery()

Builder这个类里面的where和paginate方法,就是ORM使用的方法;

其中paginate方法

    /**
     * Paginate the given query.
     *
     * @param  int  $perPage
     * @param  array  $columns
     * @param  string  $pageName
     * @param  int|null  $page
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     *
     * @throws \InvalidArgumentException
     */
    public function paginate($perPage = null, $columns = [‘*‘], $pageName = ‘page‘, $page = null)
    {
        $page = $page ?: Paginator::resolveCurrentPage($pageName);
        $perPage = $perPage ?: $this->model->getPerPage();
        $query = $this->toBase();
        $total = $query->getCountForPagination();
        $results = $total ? $this->forPage($page, $perPage)->get($columns) : new Collection;
        return new LengthAwarePaginator($results, $total, $perPage, $page, [
            ‘path‘ => Paginator::resolveCurrentPath(),
            ‘pageName‘ => $pageName,
        ]);
    }

paginate调用最后使用了LengthAwarePaginator类,

所以最后var_dump($user) 是“object(Illuminate\Pagination\LengthAwarePaginator)[306]”

LengthAwarePaginator只是分页类,与数据层无关,即Eloquent ORM和分页是分离的。

以上就是Lavavel的Eloquent ORM分页源码分析,很多地方有待深入,不过看一次源码,提高很大。

时间: 2024-10-13 10:00:50

Lavavel笔记 Eloquent ORM分页源码分析的相关文章

ORM framework源码分析:引言之Java JDBC

在百度百科上找了一段定义ORM的话:对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从效果上说,它其实是创建了一个可在编程语言里使用的"虚拟对象数据库".那么ORM Framework就是提供对象到数据库关系映射的一套编程模型.现在流行的MyBaits.Hibernate都是这种框架.本章开始我们就来分析下这两种框架源码的分析,从而更深入的

Junit学习笔记(二): 源码分析(1)

使用ModelGoon画出来的UML图如下: 图中可以分析出: 1)Test是一个接口,TestSuit和TestCase,JUnit4TestAdapter,JUnit4TestCaseFacade都实现了Test类,这一种命令模式,只需要使用Test接口就可以使用到具体的类: 2)JUnit4TestAdapter是适配器, Filterable实现了对用例的过滤, Sortable实现了对用例的排序(按照字母进行排序): 3)TestResult实现了返回结果,TestFailure记录失

Junit学习笔记(二): 源码分析(2)-命令和组合模式

命令模式 命令模式的优点: 命令模式将调用操作的对象与如何实现该操作的对象解耦. 将命令当成一个头等对象,它们可以像一般对象那样进行操纵和扩展 可以将多个命令复合成一个命令,与Composite模式结合使用 增加新的命令很容易,隔离对现有类的影响 可以与备忘录模式配合,实现撤销功能. 命令模式图: 由此带来的好处:1.客户无需使用任何条件语句去判断测试的类型,可以用统一的方式调用测试和测试套件,解除了客户与具体测试子类的耦合2.如果要增加新的TestCase也很容易,实现Test接口即可,不会影

zeromq源码分析笔记之线程间收发命令(2)

在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socket_base_t实例与session的消息通信,消息的结构由msg_t确定.命令的发送与存储是通过mailbox_t实现的,消息的发送和存储是通过pipe_t实现的,这两个结构都会详细说到,今天先说一下线程间的收发命令. zeromq的线程可分为两类,一类是io线程,像reaper_t.io_thr

OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 2013-03-23 17:44 16963人阅读 评论(28) 收藏 举报 分类: 机器视觉(34) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] KAZE系列笔记: OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 OpenCV学习笔记(28)KA

memcached学习笔记——存储命令源码分析下篇

上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制. 本文是延续上一篇,继续分析存储命令的源码.接上一篇内存分配成功后,本文主要讲解:1.memcached存储方式:2.add和set命令的区别. memcached存储方式 哈希表(HashTable) 哈希表在实践中使用的非常广泛,例如编译器通常会维护的一个符号表来保存标记,很多高级语言中也显式的支持哈希

GNU GRUB 2.00 源码分析笔记,持续更新

前言 很多运维类书籍或文章仅从系统管理者的角度讲解了 grub 的安装以及使用, 本篇博文则从 gnu grub 2.00 的源码入手,从开发者,以及系统底层运行机制的角度,分析 grub 是如何作为跨平台的"全面统一的引导加载程序",来引导操作系统,加载 Linux 内核的过程等等, 部分内容参考了<深度探索 Linux 操作系统>一书中相关的内容(ISBN 978-7-11143901-1 )以及 gnu grub 项目官方站点的文档,并且加入自己分析源码时的笔记. (

memcached学习笔记——存储命令源码分析上

原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command函数,探究memcached客户端的set命令,解读memcached是如何解析客户端文本命令,剖析memcached的内存管理,LRU算法是如何工作等等. 解析客户端文本命令 客户端向memcached server发出set操作,memcached server读取客户端的命令,客户端的连接状态

阅读《RobHess的SIFT源码分析:综述》笔记

今天总算是机缘巧合的找到了照样一篇纲要性质的文章. 如是能早一些找到就好了.不过“在你认为为时已晚的时候,其实还为时未晚”倒是也能聊以自慰,不过不能经常这样迷惑自己,毕竟我需要开始跑了! 就照着这个大纲往下走走,说不定会有意想不到的收获,然后把多视点的问题加进去,或许应该能有所成效. 嗯,其他的太多的东西想来也无用. 我觉得现在比较重要的事情是,顺着这样一篇文章继续我要做的东西. 原文<RobHess的SIFT源码分析:综述>地址: http://blog.csdn.net/masibuaa/