在 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。

一、創建文件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
{
    /**
     * 重寫頁面 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;
    }

    /**
     * 重寫當前頁設置方法
     *
     * @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;
    }

    /**
     * 將新增的分頁方法注冊到查詢構建器中,以便在模型實例上使用
     * 注冊方式:
     * 在 AppServiceProvider 的 boot 方法中注冊:AcademyPaginator::rejectIntoBuilder();
     * 使用方式:
     * 將之前代碼中在模型實例上調用 paginate 方法改為調用 seoPaginate 方法即可:
     * Article::where('status', 1)->seoPaginate(15, ['*'], 'page', page);
     */
    public static function injectIntoBuilder()
    {
        /*
         * $perPage 每頁顯示多少條
         * $columns 查詢的字段
         * $pageName 翻頁鏈接的參數名
         * $page 當前頁數
         * */
        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'
            ));
        });
    }
}

二、在 AppServiceProvider 的 boot 方法中全局調用這個注入:

// 為查詢構建器注入自己實現的分頁器方法
AcademyPaginator::injectIntoBuilder();

這樣,在模型實例上調用 seoPaginate 方法,將通過 AcademyPaginator 進行分頁。接下來,就可以在自己的代碼中編寫以下這種代碼實現偽靜態分頁鏈接了:

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

參考文檔:https://laravelacademy.org/post/9661.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM