thinkphp(tp)中分页方法paginate的学习

  • paginate
    • 获得所有数据的思考流程
      • 刚拿到数据的处理方式
      • var_dump()函数查看数据类型
      • Bootstrap部分源码
      • Paginator类部分源码
    • 基本使用(参考ThinkPHP6.0完全开发手册)
    • Bootstrap和Paginator源码(tp6)

学习初衷:不想用官方给的模板渲染方式,只想接受数据。
  • 官方给的模板渲染:
// 查询状态为1的用户数据 并且每页显示10条数据
$list = Db::name('user')->where('status',1)->paginate(10);// 渲染模板输出
return view('index', ['list' => $list]);// 模板文件中分页输出代码如下:
<div>
<ul>
{volist name='list' id='user'}
    <li> {$user.nickname}</li>
{/volist}
</ul>
</div>
{$list|raw}
  • 而我只单纯想要返回的数据
// 查询状态为1的用户数据 并且每页显示10条数据
$list = Db::name('user')->where('status',1)->paginate(10);// 只输出数据
return $list;
  • $list return 后的格式为
// 这为官方返回的格式
{
	"total": 2,
	"per_page": 10,
	"current_page": 1,
	"last_page": 1,
	"data": [
		{
		"id": 1,
		"file_id": 1,
		"acc_set": "A"
		},
		{
		"id": 2,
		"file_id": 1,
		"acc_set": "A"
		}
]
}

那么问题来了:怎么获取上图中像"total"、"data"这样的值
  先说结论:

// 取"total"值
$list -> toArray()['total']; // 返回2
// 取"data"值
$list -> toArray()['data']; // 返回aray数组

  如果你急着想知道怎么获取数据,读到这就可以结束了。
  如果你想知道我解决这个问题的思路,请继续往下看。

paginate

获得所有数据的思考流程

刚拿到数据的处理方式

{
	"total": 2,
	"per_page": 10,
	"current_page": 1,
	"last_page": 1,
	"data": [
		{
		"id": 1,
		"file_id": 1,
		"acc_set": "A"
		},
		{
		"id": 2,
		"file_id": 1,
		"acc_set": "A"
		}
]
}

  我想当然认为是数组,就用下面方式

// 取"total"值
return $list ['total'];  

  结果控制台显示 :


  说明这不是数组类型。我又想是不是json格式的,就想转为数组:

// 将json数据转为数组,第二个参数改为‘false’,则转为对象
$list = json_decode($list, true);
return $list;

  结果和上图一样,还是没有响应数据。

var_dump()函数查看数据类型

  至此,我只能通过var_dump()函数来看看你是神马?

// var_dump()函数可以查看数据类型
return var_dump($list);

  结果是这个玩意:

object(think\paginator\driver\Bootstrap)#91 (8) {
  ["simple":protected]=>
  bool(false)
  ["items":protected]=>
  object(think\Collection)#83 (1) {
    ["items":protected]=>
    array(2) {
      [0]=>
      array(3) {
        ["id"]=>
        int(1)
        ["file_id"]=>
        int(1)
        ["acc_set"]=>
        string(1) "A"
      }
      [1]=>
      array(3) {
        ["id"]=>
        int(2)
        ["file_id"]=>
        int(1)
        ["acc_set"]=>
        string(1) "A"
      }
    }
  }
  ["currentPage":protected]=>
  int(1)
  ["lastPage":protected]=>
  int(1)
  ["total":protected]=>
  int(2)
  ["listRows":protected]=>
  int(10)
  ["hasMore":protected]=>
  bool(false)
  ["options":protected]=>
  array(6) {
    ["var_page"]=>
    string(4) "page"
    ["path"]=>
    string(34) "/index.php/admin/vouch_info/search"
    ["query"]=>
    array(0) {
    }
    ["fragment"]=>
    string(0) ""
    ["list_rows"]=>
    string(2) "10"
    ["page"]=>
    int(1)
  }
}

  可以看出是一个think\paginator\driver\Bootstrap对象,由于每个属性都是protected,所以也没法直接调用($list -> total)。

  那接下来就是找源码。主要涉及两个源码:Bootstrap和Paginator(Bootstrap extends Paginator),这两个源码在文章最后。
  我找源码的方式:
    在任意控制器里使用下图方式引入,然后crtl+鼠标左键

use think\paginator\driver\Bootstrap;

Bootstrap部分源码

  这两个源码也不难,简单来说Paginator类就是对分页所有数据进行处理,而Bootstrap类是将Paginator类处理好的数据渲染到模板上。

  Bootstrap类所有方法都是生成什么什么按钮,只有一个render()方法是最核心的,他就是将按钮渲染到页面上。

/**
     * 渲染分页html
     * @return mixed
     */
    public function render()
    {
        if ($this->hasPages()) {
            if ($this->simple) {
                return sprintf(
                    '<ul class="pager">%s %s</ul>',
                    $this->getPreviousButton(), // 上一页按钮
                    $this->getNextButton()		// 下一页按钮
                );
            } else {
                return sprintf(
                    '<ul class="pagination">%s %s %s</ul>',
                    $this->getPreviousButton(),	// 上一页按钮
                    $this->getLinks(),			// 页码按钮
                    $this->getNextButton()		// 下一页按钮
                );
            }
        }
    }

  读到这,官方文档中这句话你就明白什么意思了:

// 获取分页显示
$page = $list->render();

  所以Bootstrap类比较简单,如果只想获取数据的话用不到这个类,所以要调用父类Paginator中方法。

Paginator类部分源码

  Paginator类定义了很多属性和方法,每一个都解释篇幅太长,而且我还是刚毕业且刚转正的菜鸟,估计也很难都解释清楚。这不重要,重要的是如何获得$list中的数据。直接将Paginator类中的代码拉到底,你会看到如下方法:

 /**
     * 转换为数组
     * @return array
     */
    public function toArray(): array
    {
        try {
            $total = $this->total();
        } catch (DomainException $e) {
            $total = null;
        }        return [
            'total'        => $total,
            'per_page'     => $this->listRows(),
            'current_page' => $this->currentPage(),
            'last_page'    => $this->lastPage,
            'data'         => $this->items->toArray(),
        ];
    }    /**
     * Specify data which should be serialized to JSON
     */
    public function jsonSerialize()
    {
        return $this->toArray();
    }

  下面的jsonSerialize()方法也是调用toArray(),所以toArray()方法算是提供给开发人员获取Paginator类中其他所有方法处理后得到的最终数据的接口。最后所有数据都会放在toArray()方法生成的数组中,尽情使用吧!

  当然,想使用模板渲染方式的,请参考官方手册

基本使用(参考ThinkPHP6.0完全开发手册)

TP6开发手册paginate

Bootstrap和Paginator源码(tp6)

  • Bootstrap源码
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------namespace think\paginator\driver;use think\Paginator;/**
 * Bootstrap 分页驱动
 */
class Bootstrap extends Paginator
{    /**
     * 上一页按钮
     * @param string $text
     * @return string
     */
    protected function getPreviousButton(string $text = "&laquo;"): string
    {        if ($this->currentPage() <= 1) {
            return $this->getDisabledTextWrapper($text);
        }        $url = $this->url(
            $this->currentPage() - 1
        );        return $this->getPageLinkWrapper($url, $text);
    }    /**
     * 下一页按钮
     * @param string $text
     * @return string
     */
    protected function getNextButton(string $text = '&raquo;'): string
    {
        if (!$this->hasMore) {
            return $this->getDisabledTextWrapper($text);
        }        $url = $this->url($this->currentPage() + 1);        return $this->getPageLinkWrapper($url, $text);
    }    /**
     * 页码按钮
     * @return string
     */
    protected function getLinks(): string
    {
        if ($this->simple) {
            return '';
        }        $block = [
            'first'  => null,
            'slider' => null,
            'last'   => null,
        ];        $side   = 3;
        $window = $side * 2;        if ($this->lastPage < $window + 6) {
            $block['first'] = $this->getUrlRange(1, $this->lastPage);
        } elseif ($this->currentPage <= $window) {
            $block['first'] = $this->getUrlRange(1, $window + 2);
            $block['last']  = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
        } elseif ($this->currentPage > ($this->lastPage - $window)) {
            $block['first'] = $this->getUrlRange(1, 2);
            $block['last']  = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage);
        } else {
            $block['first']  = $this->getUrlRange(1, 2);
            $block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side);
            $block['last']   = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
        }        $html = '';        if (is_array($block['first'])) {
            $html .= $this->getUrlLinks($block['first']);
        }        if (is_array($block['slider'])) {
            $html .= $this->getDots();
            $html .= $this->getUrlLinks($block['slider']);
        }        if (is_array($block['last'])) {
            $html .= $this->getDots();
            $html .= $this->getUrlLinks($block['last']);
        }        return $html;
    }    /**
     * 渲染分页html
     * @return mixed
     */
    public function render()
    {
        if ($this->hasPages()) {
            if ($this->simple) {
                return sprintf(
                    '<ul class="pager">%s %s</ul>',
                    $this->getPreviousButton(),
                    $this->getNextButton()
                );
            } else {
                return sprintf(
                    '<ul class="pagination">%s %s %s</ul>',
                    $this->getPreviousButton(),
                    $this->getLinks(),
                    $this->getNextButton()
                );
            }
        }
    }    /**
     * 生成一个可点击的按钮
     *
     * @param  string $url
     * @param  string $page
     * @return string
     */
    protected function getAvailablePageWrapper(string $url, string $page): string
    {
        return '<li><a href="' . htmlentities($url) . '">' . $page . '</a></li>';
    }    /**
     * 生成一个禁用的按钮
     *
     * @param  string $text
     * @return string
     */
    protected function getDisabledTextWrapper(string $text): string
    {
        return '<li class="disabled"><span>' . $text . '</span></li>';
    }    /**
     * 生成一个激活的按钮
     *
     * @param  string $text
     * @return string
     */
    protected function getActivePageWrapper(string $text): string
    {
        return '<li class="active"><span>' . $text . '</span></li>';
    }    /**
     * 生成省略号按钮
     *
     * @return string
     */
    protected function getDots(): string
    {
        return $this->getDisabledTextWrapper('...');
    }    /**
     * 批量生成页码按钮.
     *
     * @param  array $urls
     * @return string
     */
    protected function getUrlLinks(array $urls): string
    {
        $html = '';        foreach ($urls as $page => $url) {
            $html .= $this->getPageLinkWrapper($url, $page);
        }        return $html;
    }    /**
     * 生成普通页码按钮
     *
     * @param  string $url
     * @param  string    $page
     * @return string
     */
    protected function getPageLinkWrapper(string $url, string $page): string
    {
        if ($this->currentPage() == $page) {
            return $this->getActivePageWrapper($page);
        }        return $this->getAvailablePageWrapper($url, $page);
    }
}
  • Paginator源码
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: zhangyajun <448901948@qq.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);namespace think;use ArrayAccess;
use ArrayIterator;
use Closure;
use Countable;
use DomainException;
use IteratorAggregate;
use JsonSerializable;
use think\paginator\driver\Bootstrap;
use Traversable;/**
 * 分页基础类
 * @mixin Collection
 */
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
{
    /**
     * 是否简洁模式
     * @var bool
     */
    protected $simple = false;    /**
     * 数据集
     * @var Collection
     */
    protected $items;    /**
     * 当前页
     * @var int
     */
    protected $currentPage;    /**
     * 最后一页
     * @var int
     */
    protected $lastPage;    /**
     * 数据总数
     * @var integer|null
     */
    protected $total;    /**
     * 每页数量
     * @var int
     */
    protected $listRows;    /**
     * 是否有下一页
     * @var bool
     */
    protected $hasMore;    /**
     * 分页配置
     * @var array
     */
    protected $options = [
        'var_page' => 'page',
        'path'     => '/',
        'query'    => [],
        'fragment' => '',
    ];    /**
     * 获取当前页码
     * @var Closure
     */
    protected static $currentPageResolver;    /**
     * 获取当前路径
     * @var Closure
     */
    protected static $currentPathResolver;    /**
     * @var Closure
     */
    protected static $maker;    public function __construct($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = [])
    {
        $this->options = array_merge($this->options, $options);        $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];        $this->simple   = $simple;
        $this->listRows = $listRows;        if (!$items instanceof Collection) {
            $items = Collection::make($items);
        }        if ($simple) {
            $this->currentPage = $this->setCurrentPage($currentPage);
            $this->hasMore     = count($items) > ($this->listRows);
            $items             = $items->slice(0, $this->listRows);
        } else {
            $this->total       = $total;
            $this->lastPage    = (int) ceil($total / $listRows);
            $this->currentPage = $this->setCurrentPage($currentPage);
            $this->hasMore     = $this->currentPage < $this->lastPage;
        }
        $this->items = $items;
    }    /**
     * @access public
     * @param mixed $items
     * @param int   $listRows
     * @param int   $currentPage
     * @param int   $total
     * @param bool  $simple
     * @param array $options
     * @return Paginator
     */
    public static function make($items, int $listRows, int $currentPage = 1, int $total = null, bool $simple = false, array $options = [])
    {
        if (isset(static::$maker)) {
            return call_user_func(static::$maker, $items, $listRows, $currentPage, $total, $simple, $options);
        }        return new Bootstrap($items, $listRows, $currentPage, $total, $simple, $options);
    }    public static function maker(Closure $resolver)
    {
        static::$maker = $resolver;
    }    protected function setCurrentPage(int $currentPage): int
    {
        if (!$this->simple && $currentPage > $this->lastPage) {
            return $this->lastPage > 0 ? $this->lastPage : 1;
        }        return $currentPage;
    }    /**
     * 获取页码对应的链接
     *
     * @access protected
     * @param int $page
     * @return string
     */
    protected function url(int $page): string
    {
        if ($page <= 0) {
            $page = 1;
        }        if (strpos($this->options['path'], '[PAGE]') === false) {
            $parameters = [$this->options['var_page'] => $page];
            $path       = $this->options['path'];
        } else {
            $parameters = [];
            $path       = str_replace('[PAGE]', $page, $this->options['path']);
        }        if (count($this->options['query']) > 0) {
            $parameters = array_merge($this->options['query'], $parameters);
        }        $url = $path;
        if (!empty($parameters)) {
            $url .= '?' . http_build_query($parameters, '', '&');
        }        return $url . $this->buildFragment();
    }    /**
     * 自动获取当前页码
     * @access public
     * @param string $varPage
     * @param int    $default
     * @return int
     */
    public static function getCurrentPage(string $varPage = 'page', int $default = 1): int
    {
        if (isset(static::$currentPageResolver)) {
            return call_user_func(static::$currentPageResolver, $varPage);
        }        return $default;
    }    /**
     * 设置获取当前页码闭包
     * @param Closure $resolver
     */
    public static function currentPageResolver(Closure $resolver)
    {
        static::$currentPageResolver = $resolver;
    }    /**
     * 自动获取当前的path
     * @access public
     * @param string $default
     * @return string
     */
    public static function getCurrentPath($default = '/'): string
    {
        if (isset(static::$currentPathResolver)) {
            return call_user_func(static::$currentPathResolver);
        }        return $default;
    }    /**
     * 设置获取当前路径闭包
     * @param Closure $resolver
     */
    public static function currentPathResolver(Closure $resolver)
    {
        static::$currentPathResolver = $resolver;
    }    /**
     * 获取数据总条数
     * @return int
     */
    public function total(): int
    {
        if ($this->simple) {
            throw new DomainException('not support total');
        }        return $this->total;
    }    /**
     * 获取每页数量
     * @return int
     */
    public function listRows(): int
    {
        return $this->listRows;
    }    /**
     * 获取当前页页码
     * @return int
     */
    public function currentPage(): int
    {
        return $this->currentPage;
    }    /**
     * 获取最后一页页码
     * @return int
     */
    public function lastPage(): int
    {
        if ($this->simple) {
            throw new DomainException('not support last');
        }        return $this->lastPage;
    }    /**
     * 数据是否足够分页
     * @access public
     * @return bool
     */
    public function hasPages(): bool
    {
        return !(1 == $this->currentPage && !$this->hasMore);
    }    /**
     * 创建一组分页链接
     *
     * @access public
     * @param int $start
     * @param int $end
     * @return array
     */
    public function getUrlRange(int $start, int $end): array
    {
        $urls = [];        for ($page = $start; $page <= $end; $page++) {
            $urls[$page] = $this->url($page);
        }        return $urls;
    }    /**
     * 设置URL锚点
     *
     * @access public
     * @param string|null $fragment
     * @return $this
     */
    public function fragment(string $fragment = null)
    {
        $this->options['fragment'] = $fragment;        return $this;
    }    /**
     * 添加URL参数
     *
     * @access public
     * @param array $append
     * @return $this
     */
    public function appends(array $append)
    {
        foreach ($append as $k => $v) {
            if ($k !== $this->options['var_page']) {
                $this->options['query'][$k] = $v;
            }
        }        return $this;
    }    /**
     * 构造锚点字符串
     *
     * @access public
     * @return string
     */
    protected function buildFragment(): string
    {
        return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
    }    /**
     * 渲染分页html
     * @access public
     * @return mixed
     */
    abstract public function render();    public function items()
    {
        return $this->items->all();
    }    /**
     * 获取数据集
     *
     * @return Collection|\think\model\Collection
     */
    public function getCollection()
    {
        return $this->items;
    }    public function isEmpty(): bool
    {
        return $this->items->isEmpty();
    }    /**
     * 给每个元素执行个回调
     *
     * @access public
     * @param callable $callback
     * @return $this
     */
    public function each(callable $callback)
    {
        foreach ($this->items as $key => $item) {
            $result = $callback($item, $key);            if (false === $result) {
                break;
            } elseif (!is_object($item)) {
                $this->items[$key] = $result;
            }
        }        return $this;
    }    /**
     * Retrieve an external iterator
     * @access public
     * @return Traversable An instance of an object implementing <b>Iterator</b> or
     * <b>Traversable</b>
     */
    public function getIterator()
    {
        return new ArrayIterator($this->items->all());
    }    /**
     * Whether a offset exists
     * @access public
     * @param mixed $offset
     * @return bool
     */
    public function offsetExists($offset)
    {
        return $this->items->offsetExists($offset);
    }    /**
     * Offset to retrieve
     * @access public
     * @param mixed $offset
     * @return mixed
     */
    public function offsetGet($offset)
    {
        return $this->items->offsetGet($offset);
    }    /**
     * Offset to set
     * @access public
     * @param mixed $offset
     * @param mixed $value
     */
    public function offsetSet($offset, $value)
    {
        $this->items->offsetSet($offset, $value);
    }    /**
     * Offset to unset
     * @access public
     * @param mixed $offset
     * @return void
     * @since  5.0.0
     */
    public function offsetUnset($offset)
    {
        $this->items->offsetUnset($offset);
    }    /**
     * 统计数据集条数
     * @return int
     */
    public function count(): int
    {
        return $this->items->count();
    }    public function __toString()
    {
        return (string) $this->render();
    }    /**
     * 转换为数组
     * @return array
     */
    public function toArray(): array
    {
        try {
            $total = $this->total();
        } catch (DomainException $e) {
            $total = null;
        }        return [
            'total'        => $total,
            'per_page'     => $this->listRows(),
            'current_page' => $this->currentPage(),
            'last_page'    => $this->lastPage,
            'data'         => $this->items->toArray(),
        ];
    }    /**
     * Specify data which should be serialized to JSON
     */
    public function jsonSerialize()
    {
        return $this->toArray();
    }    public function __call($name, $arguments)
    {
        $result = call_user_func_array([$this->items, $name], $arguments);        if ($result instanceof Collection) {
            $this->items = $result;
            return $this;
        }        return $result;
    }}

第一次发csdn文章,有点激动,简单写两句话记录一下。
  以前我都是看别人的文章,没想到自己也勇敢迈出这步
  我改进了读别人文章的痛点,就是有时候找不到自己想要的结果在哪。我是上来就把结论给了,对于那些应急的读者很友好,如果想继续深入了解的,则可以继续读下去。
  你会问什么场景下会只用到Paginator 类处理好的数据:
    在前端只需要提供“第几页”和“每页多少条数据”这两个参数的情况下,利用tp自带的Paginator 类帮我们处理好返回的数据(一般最重要的数据就是参数"data"里包含的当前页的数据,前台直接拿来放到表格就行),而不需要我们自己在后端写复杂的分页逻辑。
    例如我现在的项目用tp6+ layui,layui的分页模块laypage只提供“第几页”和“每页多少条数据”这两个参数,分页逻辑要后台写。我从网上找了tp分页逻辑代码,写的都挺乱,也挺多了,所以就想着利用tp自带的Paginator 。Paginator 既然能成功的将分页渲染到模板,那他里面肯定写好了处理分页的逻辑,不用白不用。
    上段读完你可能会有一个疑问,Paginator 如何接收“第几页”和“每页多少条数据”这两个参数?

// list_rows传入“每页多少条数据”, page传入“第几页”
$list = Db::name('user')->where('status',1)->paginate([
                'list_rows'=> “每页多少条数据”,
                'page' => “第几页”,
            ]);

  最后的最后,感谢你观看。想和我一起探讨tp+html+css+js+jquery+layui相关问题的,欢迎加我qq:1853172620。我也是刚工作的菜鸟,愿我们一起共同进步。

点赞(1) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部