我们知道,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. 如何设置vmware 让别人连接你的虚拟机

    参考链接: http://blog.csdn.net/yuan1590/article/details/53504042 http://blog.csdn.net/u012110719/article ...

  2. 最短路径&次短路径算法

    容易理解:https://blog.csdn.net/m0_37345402/article/details/76695930 https://blog.csdn.net/qq_36386435/ar ...

  3. Spring重温(四)--Spring自动组件扫描

    通常情况下,声明所有的Bean类或组件的XML bean配置文件,这样Spring容器可以检测并注册Bean类或组件. 其实,Spring是能够自动扫描,检测和预定义的项目包并实例化bean,不再有繁 ...

  4. 连接字符串配置在App.config中

    <?xml version="1.0" encoding="utf-8"?> <configuration> <connectio ...

  5. 20145215《网络对抗》Exp4 恶意代码分析

    20145215<网络对抗>Exp4 恶意代码分析 基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些,用 ...

  6. k64 datasheet学习笔记12---System Integration Module (SIM)

    1.前言 Features of the SIM include: System clocking configuration(1)System clock divide values(2) Arch ...

  7. Python Web开发框架Django

    花了两周时间,利用工作间隙时间,开发了一个基于Django的项目任务管理Web应用.项目计划的实时动态,可以方便地被项目成员查看(^_^又重复发明轮子了).从前台到后台,好好折腾了一把,用到:HTML ...

  8. C++游戏开发需要阅读的书籍

    如果要自学游戏程序开发的话,可以看看下面的,呵呵. 游戏开发资料(PDF书都是中文版的,非英文,很多是本人自己扫描制作,从未网上发布过,所以独家啦):  1.Gamebryo 2.2游戏引擎(盛大.腾 ...

  9. arx对正在操作的文件进行保存

    //对正在操作的文件进行保存 STDMETHODIMP CTHDatabase::Save(void) { // TODO: 在此添加实现代码 Acad::ErrorStatus es ; try{ ...

  10. 视觉SLAM之词袋(bag of words) 模型与K-means聚类算法浅析

    原文地址:http://www.cnblogs.com/zjiaxing/p/5548265.html 在目前实际的视觉SLAM中,闭环检测多采用DBOW2模型https://github.com/d ...