翻遍ThinkPHP6官方手册为找到使用多对多关联,简单实现实现模型关联分页,当然有待优化,供大家学习参考。
解决场景是tag标签查询所有的文章news,针对news进行分页,由于标签和文章是多对多的关系,无法简单实现分页,采用先查出分页数据,再查出总数量,最后自己构建分页类实现。具体如下:
- 编写文章路由
<?php
use think\facade\Route;
//文章标签路由
Route::get('tag/:name/:page', 'Tag/index')->name('tag.index')->ext('html');
- 控制器查询获取分页数据
<?php
declare (strict_types=1);
namespace app\controller;
use app\admin\model\News;
use app\admin\model\Tags;
use app\common\Bootstrap;
class Tag extends IndexBase
{
public function index()
{
$data=$this->request->param();
$tag_name=$data['name'];
$page=(int)$data['page'];
$listRows=1;//每页显示数量
$tag=Tags::where('name',$tag_name)->with(array('news'=>function($query)use($page,$listRows){
$query->with('newsCategory')->page($page,$listRows);
}))->find();//模型关联查询数据
$list = Tags::where('name',$tag_name)->withCount('news')->find();
$results=$tag->news->toArray();//当前页数据结果集合
$total=$list->news_count;//关联查询统计数量
$news=new Bootstrap($results, $listRows, $page, $total,false,array('path'=>"/tag/{$tag_name}/1.html"));
$tag->news=$news;
return view('index',compact('tag'));
);
}
}
相关模型代码News 模型
<?php
namespace app\admin\model;
class News extends Common
{
protected $autoWriteTimestamp = true;// 开启自动写入时间戳字段
/*
* 定义了修改器之后会在下列情况下触发:
* 模型对象赋值;
* 调用模型的data方法,并且第二个参数传入true;
* 调用模型的save方法,并且传入数据;
* 显式调用模型的setAttr方法;
* 定义设置文章的描述,若为空直接截取内容前面的文字
*/
public function setAbstractAttr($abstract, $news)
{
$abstract=str_replace(' ','',$abstract);
if (empty($abstract)){
return mb_substr(strip_tags(htmlspecialchars_decode($news['content'])), 0, 100, 'utf-8');
}else{
return $abstract;
}
}
//
public function tags()
{
return $this->belongsToMany('Tags', '\\app\\admin\\model\\NewsTags', 'tags_id','news_id');
}
public function newsCategory()
{
return $this->belongsTo('NewsCategory', 'news_category_id', 'id');
}
}
相关模型代码Tags 模型
<?php
namespace app\admin\model;
class Tags extends Common
{
protected $autoWriteTimestamp = true;
protected $insert = ['hot' => 0];
public function news()
{
return $this->belongsToMany(News::class, NewsTags::class, 'news_id','tags_id');
}
}
中间表模型NewsTags
<?php
namespace app\admin\model;
use think\model\Pivot;
class NewsTags extends Pivot
{
}
- 由于使用的路由实现类似第一页
/tag/ThinkPHP/1.html
到第二页/tag/ThinkPHP/2.html
这种分页,需要修改官方分页类,代码如下:
<?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 app\common;
use think\Paginator;
/**
* Bootstrap 分页驱动
*/
class Bootstrap extends Paginator
{
/**
* 获取页码对应的链接(重写父类的url方法)
*
* @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']);
}
$url = $path;
$url=substr($url,0,strrpos($url,'/'));// /framework/1.html 截取 /framework
$url=$url.'/'.$page.'.html';
$parameters = $this->options['query'];
if (!empty($parameters)) {
$url .= '?' . http_build_query($parameters, '', '&');
}
return $url . $this->buildFragment();
}
/**
* 上一页按钮
* @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);
}
}
- 页面渲染数据
<!--lbox begin-->
<div class="lbox">
<div class="whitebg bloglist">
<h2 class="htitle"><span class="con_nav">您现在的位置是:<a href='index.html'>首页</a> > TAG信息列表 > 程序员</span>程序员</h2>
<ul>
<volist name="tag.news" id="vo">
<li>
<h3 class="blogtitle"><a href="{{:url('article.index',['id'=>$vo.id])}}" target="_blank">{{$vo.title}}</a></h3>
<span class="blogpic imgscale">
<i><a href="index10.html" target="_blank">网站公告</a></i>
<a href="{{:url('article.index',['id'=>$vo.id])}}" title="{{$vo.title}}"><img src="{{$vo.cover}}" alt="{{$vo.title}}"></a></span>
<p class="blogtext">{{$vo.abstract}}</p>
<p class="bloginfo">
<i class="avatar"><img src="/static/picture/avatar.jpg"></i>
<span></span><span></span><span>2018-11-08</span>
<span>【<a href="{{:url($vo.newsCategory.route_name,['page'=>1])}}" target="_blank">{{$vo.newsCategory.title}}</a>】</span></p>
<a href="27.html" class="viewmore">阅读更多</a> </li>
</volist>
</ul>
<!--pagelist-->
{{$tag.news|raw}}
<!--pagelist end-->
</div>
<!--bloglist end-->
</div>
实现如下分页结果。
第一页 第二页
- 代码比较繁琐,不使用模型直接使用db关联查询分页更快,或者直接使用中间表模型关联,使用中间表一对多关联文章,使用
hasWhere
过滤筛选数据,本人测试过使用hasWhere
多对多关联数据,但提示不支持改关联类型。
又更优的方式提出,欢迎大家一起交流学习。
网友评论