Laravel 把传参的分页 url 改为 /page/11

laravel 实现页面伪静态增强seo

Laravel 中通过自定义分页器分页方法实现伪静态分页链接以利于 SEO

我们知道,Laravel 自带的分页器方法包含 simplePaginate 和 paginate 方法,一个返回不带页码的分页链接,另一个返回带页码的分页链接,但是这两种分页链接页码都是以带问号的动态参数形式附加在查询字符串中,形如 https://laravelacademy.org?page=100,但是这种包含动态参数的 URL 格式对 SEO 不友好,我们最好将其转化为 https://laravelacademy.org/page/100 这种不带问号的伪静态分页链接格式,遗憾的是 Laravel 并没有提供对这种伪静态分页链接格式的自定义支持,只好自己动手了。

我们将基于 paginate 方法实现的分页进行扩展,只是修改其分页链接格式,该方法底层调用了 Illuminate\Pagination\LengthAwarePaginator 类实现分页,所以我们需要自定义一个继承自该分页器类的 app/Utils/AcademyPaginator.php,详细如下看:

任务把传参的分页url改为/page/11

http://www.24test.com/news/zhongchao?page=...

把url改为/page/11

找到了
news 路由 指向与

D:\web\work\trunk\24_project\project_test\sports-transfer\module\Article\Controller\Web\type()

type指向了 topic方法

topic 中的分页是 直接使用了

$this->paging()

paging 位于ArticlePaging的一个trait

分页的页码是直接使用了参数来获取的

$request = app(Request::class);

解决方案

改动路由,把参数改为路由占位符传参
找到路由文件
D:\web\work\trunk\24_project\project_test\sports-transfer\module\Article\Provider\RouteProvider.php--68line

我们直接加上路由参数

    Route::get("/news/{type}/page/{pages}/", "ListController@type");// pagine 改为占位符

直接如下

修改分页样式

在 article.blade.php中我们找到了引入了分页文件
位于:D:\web\work\trunk\24_project\project_test\sports-transfer\resources\views\pc\layout\block-pagination-style.blade.php

那我们在此处直接修改分页生产的url

我们需要去找到类去修改 更为合适

直接修改分页类

D:\web\work\trunk\24_project\project_test\sports-transfer\vendor\laravel\framework\src\Illuminate\Pagination\AbstractPaginator.php--line 137

加上正则的判断

/**
     * Get the URL for a given page number.
     *
     * @param  int  $page
     * @return string
     */
    // public function url($page)
    // {
    //     if ($page <= 0) {
    //         $page = 1;
    //     }

    //     // If we have any extra query string key / value pairs that need to be added
    //     // onto the URL, we will put them in query string form and then attach it
    //     // to the URL. This allows for extra information like sortings storage.
    //     $parameters = [$this->pageName => $page];

    //     if (count($this->query) > 0) {
    //         $parameters = array_merge($this->query, $parameters);
    //     }

    //     return $this->path
    //                     .(Str::contains($this->path, '?') ? '&' : '?')
    //                     .http_build_query($parameters, '', '&')
    //                     .$this->buildFragment();
    // }

    /**
     * 重写页面 URL 实现代码,去掉分页中的问号,实现伪静态链接
     * @access public 
     * @param int $page
     * @since 1.1 
     * @return string
     */
    public function url($page)
    {
        if ($page <= 0) {
            $page = 1;
        }

        // 移除路径尾部的/
        $path = rtrim($this->path, '/');

        // 如果路径中包含分页信息则正则替换页码,否则将页码信息追加到路径末尾
        if (preg_match('/\/page\/\d+/', $path)) {
            $path = preg_replace('/\/page\/\d+/', '/page/' . $page, $path);
        } else {
            $path .= '/page/' . $page;
        }
        $this->path = $path;

        if ($this->query) {
            $url = $this->path . (Str::contains($this->path, '?') ? '&' : '?')
                . http_build_query($this->query, '', '&')
                . $this->buildFragment();
        } elseif ($this->fragment) {
            $url = $this->path . $this->buildFragment();
        } else {
            $url = $this->path;
        }

        return $url;
    }

它是在这里将分页信息作为查询参数的一部分放到分页链接中,所以我们需要在自定义的 AcademyPaginator 类中重写这个方法覆盖父类实现:

/**
 * 重写页面 URL 实现代码,去掉分页中的问号,实现伪静态链接
 * @param int $page
 * @return string
 */
public function url($page)
{
    if ($page <= 0) {
        $page = 1;
    }

    // 移除路径尾部的/
    $path = rtrim($this->path, '/');

    // 如果路径中包含分页信息则正则替换页码,否则将页码信息追加到路径末尾
    if (preg_match('/\/page\/\d+/', $path)) {
        $path = preg_replace('/\/page\/\d+/', '/page/' . $page, $path);
    } else {
        $path .= '/page/' . $page;
    }
    $this->path = $path;

    if ($this->query) {
        $url = $this->path . (Str::contains($this->path, '?') ? '&' : '?')
            . http_build_query($this->query, '', '&')
            . $this->buildFragment();
    } elseif ($this->fragment) {
        $url = $this->path . $this->buildFragment();
    } else {
        $url = $this->path;
    }

    return $url;
}

至此,我们自定义的分页器类已经编写好了,接下来还要将这个分页器注册到模型类的查询构建器中以便可以像simplePaginate 和 paginate 方法那样在模型实例上调用,这里我们需要借助查询构建器 Illuminate\Database\Eloquent\Builder 提供的静态 macro 方法在运行时动态扩展其提供的方法,我们还是在 AcademyPaginator 中定义这个方法扩展实现:

/**
 * 将新增的分页方法注册到查询构建器中,以便在模型实例上使用
 * 注册方式:
 * 在 AppServiceProvider 的 boot 方法中注册:AcademyPaginator::rejectIntoBuilder();
 * 使用方式:
 * 将之前代码中在模型实例上调用 paginate 方法改为调用 seoPaginate 方法即可:
 * Article::where('status', 1)->seoPaginate(15, ['*'], 'page', page);
 */
public static function injectIntoBuilder()
{
    Builder::macro('seoPaginate', function ($perPage, $columns, $pageName, $page) {
        $perPage = $perPage ?: $this->model->getPerPage();

        $items = ($total = $this->toBase()->getCountForPagination())
            ? $this->forPage($page, $perPage)->get($columns)
            : $this->model->newCollection();

        $options = [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => $pageName,
        ];

        return Container::getInstance()->makeWith(AcademyPaginator::class, compact(
            'items', 'total', 'perPage', 'page', 'options'
        ));
    });
}

这样,在模型实例上调用 seoPaginate 方法,将通过 AcademyPaginator 进行分页,最后我们在 AppServiceProvider 的 boot 方法中全局调用这个注入:

// 为查询构建器注入自己实现的分页器方法
AcademyPaginator::injectIntoBuilder();
接下来,就可以在自己的代码中编写以下这种代码实现伪静态分页链接了:

$articles = Article::with('author', 'category')->public()->orderBy('id', 'desc')->seoPaginate($pageSize, $this->listColumns, 'page', $page);

致谢

https://www.cnblogs.com/liangzia/p/6223129...

为啥子 只要使用seoPaginate?

因为

// 为查询构建器注入自己实现的分页器方法
        // Builder 提供的静态macro 方法在运行时动态扩展 所以我们实现时候只要
        // 将之前代码中在模型实例上调用 paginate 方法改为调用 seoPaginate 方法即可:
        // @author bobo
        // @date 2019-11-01

解决问题记录

本人代码是自己写的实现分页方法 直接new到了 laravel底层的类传入属性

那我们这里不可以调用属性seoPaginate

解决方法:

直接new AcademyPaginator 类传入属性即可

/**
     * @param $articles Collection
     * @param int $pn
     * @return LengthAwarePaginator
     */
    /** 
    * topic  
    * 分页 占位符方式传参
    * 此处方法为兼容版本 担心其他地方调用报错
    * @access public 
    * @param mixed $arg1 列表页数据类型 
    * @param int $arg2 分页条目数 默认为空 
    * @param int $arg3 页码 默认为空 
    * @date 2019-11-01
    * @author bobo<1576554465@qq.com>
    * @version     $1.1$ 
    * @since 1.0 
    * @return html 
    */ 
    public function paging($articles, $pn = 10,$page=null) {

        $request = app(Request::class);
        // dd($request);
        // 拿到页码  request有 
        if ($request->has('page')) {
            $pl = $request->input('page');
            $pl = $pl <= 0 ? 1 :$pl;
        } elseif(!empty($page)) {
            // 传递的页码不为空则直接赋值
            $pl = $page;
            $pl = $pl <= 0 ? 1 :$pl;
        } else {
            $pl = 1;
        }
        // $object = new LengthAwarePaginator($articles->slice(($pl-1)*$pn, $pn), $articles->count(), $pn, $pl,[
        // 使用新方法seoPaginate 
        $object = new AcademyPaginator($articles->slice(($pl-1)*$pn, $pn), $articles->count(), $pn, $pl,[
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => 'page',
        ]);
        $object->clazz = 'page';

        return $object;
    }

排查其他受影响的url

基本不影响,因为我们是适配使用的,只要你使用的是laravel提供的分页类即可。
而且路由没有进行大改

本作品采用《CC 协议》,转载必须注明作者和本文链接
嗨,我是波波。曾经创业,有收获也有损失。我积累了丰富教学与编程经验,期待和你互动和进步! 公众号:上海PHP自学中心 付费知识星球:破解面试:程序员的求职导师
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2
Epona

为了SEO也是拼了命了。。

4年前 评论
wangchunbo

@Epona 哈哈没办法

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
司机 @ 欣昊玉
文章
273
粉丝
339
喜欢
558
收藏
1106
排名:64
访问:12.2 万
私信
所有博文
社区赞助商