美文网首页
ThinkPHP6.0模型关联自定义分页实现 多对多关联分页

ThinkPHP6.0模型关联自定义分页实现 多对多关联分页

作者: 思议岁月 | 来源:发表于2019-12-14 17:54 被阅读0次

    翻遍ThinkPHP6官方手册为找到使用多对多关联,简单实现实现模型关联分页,当然有待优化,供大家学习参考。
    解决场景是tag标签查询所有的文章news,针对news进行分页,由于标签和文章是多对多的关系,无法简单实现分页,采用先查出分页数据,再查出总数量,最后自己构建分页类实现。具体如下:

    1. 编写文章路由
    <?php
    use think\facade\Route;
    //文章标签路由
    Route::get('tag/:name/:page', 'Tag/index')->name('tag.index')->ext('html');
    
    1. 控制器查询获取分页数据
    <?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
    {
    
    }
    
    1. 由于使用的路由实现类似第一页/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);
        }
    }
    
    
    1. 页面渲染数据
    <!--lbox begin-->
            <div class="lbox">
    
                <div class="whitebg bloglist">
                    <h2 class="htitle"><span class="con_nav">您现在的位置是:<a href='index.html'>首页</a>&nbsp;>&nbsp;TAG信息列表&nbsp;>&nbsp;程序员</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>
    

    实现如下分页结果。


    第一页 第二页
    1. 代码比较繁琐,不使用模型直接使用db关联查询分页更快,或者直接使用中间表模型关联,使用中间表一对多关联文章,使用hasWhere过滤筛选数据,本人测试过使用hasWhere多对多关联数据,但提示不支持改关联类型。
      又更优的方式提出,欢迎大家一起交流学习。

    相关文章

      网友评论

          本文标题:ThinkPHP6.0模型关联自定义分页实现 多对多关联分页

          本文链接:https://www.haomeiwen.com/subject/nbocnctx.html