修改larave分页地址,不带分号,seo

2019-12-29 15:04:43

参考地址 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 提供的分页类即可。
而且路由没有进行大改


  • 2019-08-22 13:35:27

    Generator函数的语法

    执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了是状态机还是一个遍历器对象生成函数。 返回遍历器对象,可以依次遍历Generator函数内部的每一个状态。

  • 2019-08-22 16:38:15

    理解JS原型对象与原型链(重要清晰)

    JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象对应拥有一个原型,对象以其原型为模板、从原型继承方法和属性。而同时原型也是对象,它也拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

  • 2019-08-22 17:26:21

    详解javaScript的深拷贝

    最开始意识到深拷贝的重要性是在我使用redux的时候(react + redux), redux的机制要求在reducer中必须返回一个新的对象,而不能对原来的对象做改动,事实上,当时我当然不会主动犯这个错误,但很多时候,一不小心可能就会修改了原来的对象,例如:var newObj = obj; newObj.xxx = xxx 实际上,这个时候newObj和obj两个引用指向的是同一个对象,我修改了newObj,实际上也就等同于修改了obj,这,就是我和深浅拷贝的第一次相遇。

  • 2019-08-22 19:14:21

    Android Studio 3.5最新特性

    Android Studio(以下简称为AS) 3.5正式版终于发布了,从第一个bate版本发布到正式版本,历时三个半月。AS一直以来被开发者吐槽,因此谷歌也放慢了版本的变化,对测试版本进行大力度的优化,提高了稳定性。从3.3版本开始,谷歌启动了名为Project Marble的计划,意为谷歌团队致力于使集成开发环境(IDE)的基本功能和流程变得坚如磐石,同时精炼和完善面向用户的功能。而AS 3.5则是Project Marble主要成果的版本,下面来介绍主要成果。

  • 2019-08-27 05:43:13

    Laravel 门面自动补全工具 laravel-ide-helper

    当我们在 PhpStorm 编辑器中,开发 Laravel 框架的项目时,很多类方法都不能自动补全和定位,比如 Facade 门面的方法,DB::table()、Route::get() 等。

  • 2019-08-28 08:28:36

    Js apply,call方法详解,及其apply()方法的妙用

    在给对象参数的情况下,如果参数的形式是数组的时候,比如apply示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就可以采用 apply , 如果我的Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call来实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));