laravel 去重(distinct)后分页(paginate)

Posted by 昆山吴彦祖 on 2018.05.17

laravel 自带的分页系统paginate 非常好用,但是当遇到去重的时候,自带的分页就是出现一些小麻烦,页面总数total还是没有去重之前的total
$blogs = DB::table('blogs')->distinct()->select('blogs.id','blogs.title','blogs.created_at','blogs.description','blogs.thumb')->join('blog_category', 'blogs.id', '=', 'blog_category.blog_id')->whereIn('blog_category.blog_category_id',$CategoryChildren)->orderBy('blogs.created_at','desc')->paginate(4);

如上面这个数据,即使前面加了 distinct和select,系统计算出的总数仍然是未去重的。

这时候执行的sql语句(计算total的)为:

select count(*) as aggregate from `blogs` inner join `blog_category` on `blogs`.`id` = `blog_category`.`blog_id` where `blog_category`.`blog_category_id` in (10, 11, 12)


网上搜索了很久没找到相关资料,包括官方文档(中文)中也没有提及。最后只有自己看源码

网站根目录\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php

找到了paginate  方法,

public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
    {
        $page = $page ?: Paginator::resolveCurrentPage($pageName);

        $total = $this->getCountForPagination($columns);

        $results = $total ? $this->forPage($page, $perPage)->get($columns) : collect();

        return $this->paginator($results, $total, $perPage, $page, [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => $pageName,
        ]);
    }
public function getCountForPagination($columns = ['*'])
    {
        $results = $this->runPaginationCountQuery($columns);

        // Once we have run the pagination count query, we will get the resulting count and
        // take into account what type of query it was. When there is a group by we will
        // just return the count of the entire results set since that will be correct.
        if (isset($this->groups)) {
            return count($results);
        } elseif (! isset($results[0])) {
            return 0;
        } elseif (is_object($results[0])) {
            return (int) $results[0]->aggregate;
        }

        return (int) array_change_key_case((array) $results[0])['aggregate'];
    }
protected function runPaginationCountQuery($columns = ['*'])
    {
        return $this->cloneWithout(['columns', 'orders', 'limit', 'offset'])
                    ->cloneWithoutBindings(['select', 'order'])
                    ->setAggregate('count', $this->withoutSelectAliases($columns))
                    ->get()->all();
    }

而计算total数量就是利用了 count方法。

我们把之前失败的方法改为下面即可:

$blogs = DB::table('blogs')->distinct()->select('blogs.id','blogs.title','blogs.created_at','blogs.description','blogs.thumb')->join('blog_category', 'blogs.id', '=', 'blog_category.blog_id')->whereIn('blog_category.blog_category_id',$CategoryChildren)->orderBy('blogs.created_at','desc')->paginate(4);

paginate 这个方法其实还可以接受一个数组参数,当有数组参数的时候,sql语句会变为: 

select count(distinct `id`) as aggregate from `blogss` inner join `blog_category` on `blogs`.`id` = `blog_category`.`blog_id` where `blog_category`.`blog_category_id` in (10, 11, 12)


 

分页