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 = "«"): 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 = '»'): 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。我也是刚工作的菜鸟,愿我们一起共同进步。
发表评论 取消回复