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

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

<?php

namespace App\Utils;

use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator as BasePaginator; class AcademyPaginator extends BasePaginator
{
}

在 Laravel 自带的分页器中,默认分页链接是通过 url 方法生成并返回的,该方法定义在抽象类 vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php 中:

/**
* 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();
}

它是在这里将分页信息作为查询参数的一部分放到分页链接中,所以我们需要在自定义的 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;
}

实现原理很简单,就是判断页面路径中是否已经包含类似 /page/100 这种页码信息,如果不包含,则将其添加到路径中,否则对页码值进行正则替换,最后把新的路径结合之前的查询参数和锚点信息拼接为新的完整分页链接返回给调用方。

除此之外,因为分页链接格式变了,我们还要在 AcademyPaginator 对父类设置当前页码的方法进行重写,还是一个对当前分页链接的正则匹配:

/**
* 重写当前页设置方法
*
* @param int $currentPage
* @param string $pageName
* @return int
*/
protected function setCurrentPage($currentPage, $pageName)
{
if (!$currentPage && preg_match('/\/page\/(\d+)/', $this->path, $matches)) {
$currentPage = $matches[1];
} return $this->isValidPageNumber($currentPage) ? (int) $currentPage : 1;
}

至此,我们自定义的分页器类已经编写好了,接下来还要将这个分页器注册到模型类的查询构建器中以便可以像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);

Laravel 中通过自定义分页器分页方法实现伪静态分页链接以利于 SEO的更多相关文章

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

    我们知道,Laravel 自带的分页器方法包含 simplePaginate 和 paginate 方法,一个返回不带页码的分页链接,另一个返回带页码的分页链接,但是这两种分页链接页码都是以带问号的动 ...

  2. 详解如何在Laravel中增加自定义全局函数

    http://www.php.cn/php-weizijiaocheng-383928.html 如何在Laravel中增加自定义全局函数?在我们的应用里经常会有一些全局都可能会用的函数,我们应该怎么 ...

  3. django中的自定义分页器

    1.什么是自定义分页器 当我们需要在前端页面展示的数据太多的时候,我们总不能将数据展示在一页上面吧!这时,我们就需要自定义一个分页器,将数据分成特定的页数进行展示,每一页展示固定条数的数据! 2.为什 ...

  4. Javascript 中创建自定义对象的方法(设计模式)

    Javascript 中创建对象,可以有很多种方法. Object构造函数/对象字面量: 抛开设计模式不谈,使用最基本的方法,就是先调用Object构造函数创建一个对象,然后给对象添加属性. var ...

  5. php在laravel中使用自定义的Common类

    众所周知,laravel是一款高度集成的开发框架,框架内置非常多的操作方法,从而保证了我们的开发效率.但是在日常中为了满足我们的个性化业务,也需要自己去编写工具类,在laravel中我们完成编写后还需 ...

  6. JavaScript中创建自定义对象的方法

    本文内容参考JavaScript高级程序设计(第3版)第6章:面向对象的程序设计 ECMA-262中把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”我所理解的就是对象就是一个结构 ...

  7. Laravel中利用队列发送邮件的方法示例

    https://www.jb51.net/article/121647.htm 本文主要给大家介绍了关于Laravel中队列发送邮件的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的 ...

  8. delphi中使用自定义资源的方法

    如果要在delphi中使用自定义资源文件*.res文件,比如一个光标,此时可以采用下列步骤: 1,创建包含相应的资源文件,这里是创建一个包含自定义光标的res文件. 2,在主窗体的pas文件中加入编译 ...

  9. 纯JS前端分页方法(JS分页)

    1.JS分页函数:开发过程中,分页功能一般是后台提供接口,前端只要传page(当前页码)和pageSize(每页最大显示条数)及对应的其他查询条件,就可以返回所需分页显示的数据. 但是有时也需要前端本 ...

随机推荐

  1. python 的基础 学习 第五天 基础数据类型的操作方法

    1,列表的基本操作方法 1,列表是python中的基础数据类型之一,其他语言中也有类似于列表的数据类型,比如js中叫数组,他是以[ ]括起来,每个元素以逗号隔开,而且他里面可以存放各种数据类型比如: ...

  2. 《从Paxos到Zookeeper:分布式一致性原理与实践》第一章读书笔记

    第一章主要介绍了计算机系统从集中式向分布式系统演变过程中面临的挑战,并简要介绍了ACID.CAP和BASE等经典分布式理论,主要包含以下内容: 集中式的特点 分布式的特点 分布式环境的各种问题 ACI ...

  3. Java8新特性(待更新...)

    一.Lambda表达式 二.接口的默认方法与静态方法 三.方法引用 四.重复注解 五.扩展注解的支持 六.Optional 七.Stream 八.Date/Time API (JSR 310) 九.J ...

  4. 高性能IO之Reactor模式

    The reactor design pattern is an event handling pattern for handling service requests delivered conc ...

  5. json数据交互

    springmvc 的json数据交互 - 哎幽的成长 - CSDN博客--和老师讲课相同http://blog.csdn.net/u012373815/article/details/4720818 ...

  6. TPU使用说明

    1 TPU分类和收费标准 1.1 分类和计费说明 地区 抢占式TPU Cloud TPU 美国 $1.35/hour $4.5/hour 欧洲 $1.485/hour $4.95/hour 亚太区地区 ...

  7. mysql进制之间的转换

    1.十进制转换成二进制 select bin(5); 2.十进制转换成八进制 select oct(5); 3.十进制转换成十六进制 select hex(5); 4.二进制转换成十进制 select ...

  8. jquery知识巩固

    1.jquery中的index方法和eq方法 index()方法:index()获取的索引值是相对同兄弟元素,即同一个父元素,,所以首先要确认一下这个元素的兄弟是谁,例如: 当遇到以上的情况很容易就觉 ...

  9. flask(1)

    在Python中常用的web框架有flask.Django.tornado # -*- encoding: utf-8 -*- from flask import Flask #建立Flask对象 a ...

  10. Holedox Eating HDU - 4302 2012多校C 二分查找+树状数组/线段树优化

    题意 一个长度$n<=1e5$的数轴,$m<=1e5$个操作 有两种一些操作 $0$  $x$ 在$x$放一个食物 $1$ 一个虫子去吃最近的食物,如果有两个食物一样近,不转变方向的去吃 ...