技术专区 \ PHP

深入Laravel : 扩展替换Laravel 默认分页模板

分页构造器 Laravel-Paginator Laravel Pagination ⋅ 2017-05-16 12:14:35

注:Laravel 5.4之后,Laravel提供了系统内置的支持,请参考:http://laravelacademy.org/post/6960.html

 

在使用 Laravel 分页功能时,需要配合使用前端框架 Bootstrap 的分页组件,这两者结合甚为紧密。可是由于我的博客更换为 AmazeUI,而 AmazeUI 分页组件使用的 CSS 类选择器是 .am-pagination 而不是 Bootstrap 的 .pagination。那么如何在不修改框架代码的前提下替换默认的 Laravel Pagination 生成的 html 模板呢?本文将通过利用 Laravel Service Provider (服务提供者)实现该需求。通过了解本文的内容大家也可以实现更多的分页样式。

关于 Laravel 的 Pagination (分页)组件

Laravel 的分页组件其实从一开始就考虑到这个需求,但是文档上根本就没有写如何操作,导致很多人误解 Laravel 的分页组件并不适用于复杂场景。其实通过查阅 API 文档或者直接查看 Illuminate\Pagination\Paginator 类,可以发现 laravel 的分页组件是十分灵活的。查看该类的 render 方法,可以注意到实际生成分页 html 模板的是一个实现 Illuminate\Contracts\Pagination\Presenter 接口的实例,我们只需要实现一个基于该接口的类,并传入该方法即可。

实现过程

创建必要的类

通过分析,我们首先要创建一个实现 Illuminate\Contracts\Pagination\Presenter 接口的实例。本文中案例中,我们在框架 app 目录下创建了一个类 NewPaginationPresenter, 命名空间是 App,该类实现上面我们讲的接口。

命名空间为什么是 App?因为 laravel 默认的,在 App 目录下的类命名空间一般都是 App(当然通过 artisan 命令修改过则以修改的为准),之后的 子命名空间 都按照 PSR-4 规范对应。

实现必要的方法

接口要求必须实现 hasPage 和 render 方法,其中 render 方法返回的值就是我们生成的目标 html 模板,因此我们主要就是实现这个方法。

对于 Laravel 默认的分页 html 代码的生成,是由 Illuminate\Pagination\BootstrapThreePresenter 类完成的,该类正是实现了之前我们说的那个接口,由于我们仅需要修改生成的 html 中的 dom 的 class 属性,所以我们没必要完完全全重写,只需要继承 Illuminate\Pagination\BootstrapThreePresenter 类,然后对其中的部分方法稍微改造即可

实际上我们通过文档也看得出一些端倪,laravel 的分页组件有 paginate 和 simplePaginate 两个方法,实际上这两个方法分别通过 Illuminate\Pagination 命名空间下的 BootstrapThreePresenter 类和 SimpleBootstrapThreePresenter 类生成目标 html,而 SimpleBootstrapThreePresenter 实际上只是继承 BootstrapThreePresenter,仅仅修改了其父类的两个方法而已。

我们创建的类同样可以继承 Illuminate\Pagination\BootstrapThreePresenter 类,我们的目的是让其生成符合 AmazeUI 分页组件的 html 代码格式。

默认 Illuminate\Pagination\BootstrapThreePresenter 有这样几个方法:

class BootstrapThreePresenter implements PresenterContract
{

// 此处省略

    public function render()
    {
        if ($this->hasPages()) {
            return sprintf(
                '<ul class="pagination">%s %s %s</ul>',
                $this->getPreviousButton(),
                $this->getLinks(),
                $this->getNextButton()
            );
        }

        return '';
    }

    protected function getAvailablePageWrapper($url, $page, $rel = null)
    {
        $rel = is_null($rel) ? '' : ' rel="' . $rel . '"';

        return '<li><a href="' . htmlentities($url) . '"' . $rel . '>' . $page . '</a></li>';
    }

    protected function getDisabledTextWrapper($text)
    {
        return '<li class="disabled"><span>' . $text . '</span></li>';
    }

    protected function getActivePageWrapper($text)
    {
        return '<li class="active"><span>' . $text . '</span></li>';
    }

// 此处省略

}

通过发现,我们发现只需要修改上述代码中出现的 getActivePageWrapper、 getDisabledTextWrapper、 render 方法中的关于生成 html 的内容即可,比如 AmazeUI 的分页 html 结构和 Bootsrtap 一致,只是 CSS 选择器由 Bootstrap 的 .pagination 变为 .am-pagination 而已,那么我们就将 render 方法中的 .pagination 改为 .am-pagination 即可。其余的替换方式类似。

当然对于需求更多,则可以对 render 方法整体改造,总而言之,生成的 html 由该方法返回即可,至于生成什么、逻辑是什么样子的则由你来决定。

替换默认的生成类

我们知道,Pagination 组件的 render 方法实际上是调用 Presenter 接口实现的实例的 render 方法,通过将实例传入 render 即可。但是这样并不科学,首先是我们上面实现的 Presenter 实例的构造函数需要其他参数,同时还需要 Pagination 实例的传入!

这样看来十分麻烦且不实用。

但我们发现 Pagination 类有一个静态方法 presenter,该方法的作用就是注册默认 Presenter 实例,我们可以在任意位置(当然必须要在使用 Pagination 的 render 方法前)通过该静态方法注册默认 Presenter 实例即可。

为了方便全局使用,我们可以通过使用 Service Provider 在框架启动之初就注册该实例。

通过 artisan 创建一个 Service Provider,在 boot 方法中这样写:

<?php namespace App\Providers;

use App\NewPaginationPresenter;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Support\ServiceProvider;

class PaginationProvider extends ServiceProvider
{

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
// 使用自定义分页模板
        Paginator::presenter(function (AbstractPaginator $paginator) {
            return new NewPaginationPresenter($paginator);
        });
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
//
    }
}

保存后,将该 Service Provider 加入 config 目录下的 app.php 配置文件的 providers 项里。好了,大功告成!!!

 

来源:https://www.insp.top/article/replace-laravel-pagination-default-template