ThinkPHP数组分页功能详解


[TOC]

原理

首先找到随便一段PHP查询代码带有paginate的,例如

$data_list = UserModel::where($map)->order('sort,role,id desc')->paginate();

Ctrl+LeftClick点进去看看:

Model.php

会发现进入到了Model.php文件:
见名思意,该文件是MVC框架的Model实现,不过注意这里:

 * @method Paginator|$this paginate() static 分页

拉到顶部可以看到如下:

/**
 * Class Model
 * @package think
 * @mixin Query
 * @method $this scope(string|array $scope) static 查询范围
 * @method $this where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
 * @method $this whereRaw(string $where, array $bind = [], string $logic = 'AND') static 表达式查询
 * @method $this whereExp(string $field, string $condition, array $bind = [], string $logic = 'AND') static 字段表达式查询
 * @method $this when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
 * @method $this join(mixed $join, mixed $condition = null, string $type = 'INNER', array $bind = []) static JOIN查询
 * @method $this view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
 * @method $this with(mixed $with, callable $callback = null) static 关联预载入
 * @method $this count(string $field = '*') static Count统计查询
 * @method $this min(string $field, bool $force = true) static Min统计查询
 * @method $this max(string $field, bool $force = true) static Max统计查询
 * @method $this sum(string $field) static SUM统计查询
 * @method $this avg(string $field) static Avg统计查询
 * @method $this field(mixed $field, boolean $except = false, string $tableName = '', string $prefix = '', string $alias = '') static 指定查询字段
 * @method $this fieldRaw(string $field) static 指定查询字段
 * @method $this union(mixed $union, boolean $all = false) static UNION查询
 * @method $this limit(mixed $offset, integer $length = null) static 查询LIMIT
 * @method $this order(mixed $field, string $order = null) static 查询ORDER
 * @method $this orderRaw(string $field, array $bind = []) static 查询ORDER
 * @method $this cache(mixed $key = null , integer|\DateTime $expire = null, string $tag = null) static 设置查询缓存
 * @method mixed value(string $field, mixed $default = null) static 获取某个字段的值
 * @method array column(string $field, string $key = '') static 获取某个列的值
 * @method $this find(mixed $data = null) static 查询单个记录
 * @method $this findOrFail(mixed $data = null) 查询单个记录
 * @method Collection|$this[] select(mixed $data = null) static 查询多个记录
 * @method $this get(mixed $data = null,mixed $with = [],bool $cache = false, bool $failException = false) static 查询单个记录 支持关联预载入
 * @method $this getOrFail(mixed $data = null,mixed $with = [],bool $cache = false) static 查询单个记录 不存在则抛出异常
 * @method $this findOrEmpty(mixed $data = null) static 查询单个记录  不存在则返回空模型
 * @method Collection|$this[] all(mixed $data = null,mixed $with = [],bool $cache = false) static 查询多个记录 支持关联预载入
 * @method $this withAttr(array $name,\Closure $closure = null) static 动态定义获取器
 * @method $this withJoin(string|array $with, string $joinType = '') static
 * @method $this withCount(string|array $relation, bool $subQuery = true) static 关联统计
 * @method $this withSum(string|array $relation, string $field, bool $subQuery = true) static 关联SUM统计
 * @method $this withMax(string|array $relation, string $field, bool $subQuery = true) static 关联MAX统计
 * @method $this withMin(string|array $relation, string $field, bool $subQuery = true) static 关联Min统计
 * @method $this withAvg(string|array $relation, string $field, bool $subQuery = true) static 关联Avg统计
 * @method Paginator|$this paginate() static 分页
 */

也就是说啊,这个Model.php的方法是从Query.php这里搞过来的,那我们来看一下Query.php里面是什么

Query.php

Query.php文件主要是针对数据库链式操作的类,不过其中含有这么一个方法:

/**
     * 分页查询
     * @access public
     * @param  int|array $listRows 每页数量 数组表示配置参数
     * @param  int|bool  $simple   是否简洁模式或者总记录数
     * @param  array     $config   配置参数
     *                            page:当前页,
     *                            path:url路径,
     *                            query:url额外参数,
     *                            fragment:url锚点,
     *                            var_page:分页变量,
     *                            list_rows:每页数量
     *                            type:分页类名
     * @return $this[]|\think\Paginator
     * @throws DbException
     */
    public function paginate($listRows = null, $simple = false, $config = [])

那么这也就是说,我们之前的代码:

$data_list = UserModel::where($map)->order('sort,role,id desc')->paginate();

其方法->paginate();是调用的Query.php中的paginate方法。

DIY实现分页

解释

如果我们想自己查询数据库,例如使用->select();方法:

$data_list = UserModel::where($map)->order('sort,role,id desc')->select();

没错,得到的$data_list是数组类型,数组类型并不返回Model本身的实例,则无法继续像这样调用paginate方法了:

$data_list = UserModel::where($map)->order('sort,role,id desc')->select();
$data_list->paginate();    //    这里是错误的,因为数组没有paginate方法可调用

怎么办?虽然无法调用了,但是我们可以自己创造实例去呀?
但是我们需要先了解paginate方法的类在哪对吧?

Paginator.php

根据查询,弄到了paginate的方法原类对象是Paginator,所在位置thinkphp/library/think/Paginator.php

它是一个abstract抽象类,抽象类是不能实例化的。

abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable

不过我们可以提前看看它所提供的方法:

public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])

这两个方法,一个是用于构造对象用的构造函数__construct,另外一个是静态方法make,看参数应该知道,这就是我们想要找的类。

Bootstrap.php

再来看看Bootstrap,这个类继承自Paginator抽象类,也就是Paginator抽象类的实现类,

class Bootstrap extends Paginator

所在位置:thinkphp/library/think/paginator/driver/Bootstrap.php

在Query.php中是这么实例化它的:

$config = Container::get('config')->pull('paginate');

/** @var Paginator $class */
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);

return $class::make($results, $listRows, $page, $total, $simple, $config);

$config['type']来自于thinkphp/convention.php配置文件的:

//分页配置
'paginate'   => [
    'type'      => 'bootstrap',
    'var_page'  => 'page',
    'list_rows' => 15,
],

所以,我们接下来可以自己去创造实例去了。

自己创造实例

根据以上知识,我们只需要自行实例化Bootstrap类,并将查询的数组数据注入至Bootstrap实例,最后将Bootstrap实例交给ThinkPHP来处理就好了,非常简单。

$data_list = UserModel::where($map)->order('sort,role,id desc')->select();
$config = Container::get('config')->pull('paginate');
/*

$listRows 每页数量 数组表示配置参数
$simple   是否简洁模式或者总记录数
$config 配置参数
    page:当前页,
    path:url路径,
    query:url额外参数,
    fragment:url锚点,
    var_page:分页变量,
    list_rows:每页数量
    type:分页类名
*/

// 原型:
// public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])

$data_list = Bootstrap::make($data_list,$config['list_rows'],1,15,false,$config);

此时,$data_list就拥有了原数组就有了Bootstrap实例的外衣,交给ThinkPHP去处理时,就可以分页了。

添加新评论