ThinkPHP数组分页功能详解
ThinkPHP数组分页功能详解
[TOC]
原理
首先找到随便一段PHP查询代码带有paginate的,例如
1 | $data_list = UserModel::where( $map )->order( 'sort,role,id desc' )->paginate(); |
Ctrl+LeftClick点进去看看:
Model.php
会发现进入到了Model.php文件:
见名思意,该文件是MVC框架的Model实现,不过注意这里:
1 | * @method Paginator| $this paginate() static 分页 |
拉到顶部可以看到如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /** * 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文件主要是针对数据库链式操作的类,不过其中含有这么一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * 分页查询 * @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 = []) |
那么这也就是说,我们之前的代码:
1 | $data_list = UserModel::where( $map )->order( 'sort,role,id desc' )->paginate(); |
其方法->paginate();是调用的Query.php中的paginate方法。
DIY实现分页
解释
如果我们想自己查询数据库,例如使用->select();方法:
1 | $data_list = UserModel::where( $map )->order( 'sort,role,id desc' )->select(); |
没错,得到的$data_list是数组类型,数组类型并不返回Model本身的实例,则无法继续像这样调用paginate方法了:
1 2 | $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抽象类,抽象类是不能实例化的。
1 | abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable |
不过我们可以提前看看它所提供的方法:
1 2 | 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抽象类的实现类,
1 | class Bootstrap extends Paginator |
所在位置:thinkphp/library/think/paginator/driver/Bootstrap.php
在Query.php中是这么实例化它的:
1 2 3 4 5 6 | $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配置文件的:
1 2 3 4 5 6 | //分页配置 'paginate' => [ 'type' => 'bootstrap' , 'var_page' => 'page' , 'list_rows' => 15, ], |
所以,我们接下来可以自己去创造实例去了。
自己创造实例
根据以上知识,我们只需要自行实例化Bootstrap类,并将查询的数组数据注入至Bootstrap实例,最后将Bootstrap实例交给ThinkPHP来处理就好了,非常简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $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去处理时,就可以分页了。