База даних: Пагінація
- Вступ
- Основи використання
- Відображення результатів пагінації
- Налаштування представлення пагінації
- Методи екземпляра Paginator та LengthAwarePaginator
- Методи екземпляра Cursor Paginator
Вступ
В інших фреймворках пагінація може бути дуже складною. Ми сподіваємося, що підхід Laravel до пагінації стане ковтком свіжого повітря. Пагінатор Laravel інтегрований з конструктором запитів та Eloquent ORM і забезпечує зручну, легку у використанні пагінацію записів бази даних без жодної конфігурації.
За замовчуванням, HTML, згенерований пагінатором, сумісний з фреймворком Tailwind CSS; однак, підтримка пагінації Bootstrap також доступна.
Tailwind
Якщо ви використовуєте стандартні представлення пагінації Tailwind у Laravel з Tailwind 4.x, файл resources/css/app.css вашого застосунку вже буде належним чином налаштований для @source представлень пагінації Laravel:
@import 'tailwindcss';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
Основне використання
Розбиття результатів конструктора запитів
Існує кілька способів пагінації елементів. Найпростіший - це використання методу paginate на конструкторі запитів або в Eloquent-запиті. Метод paginate автоматично піклується про встановлення "limit" і "offset" запиту на основі поточної сторінки, яку переглядає користувач. За замовчуванням поточна сторінка визначається значенням аргументу рядка запиту page у HTTP-запиті. Це значення автоматично визначається Laravel і також автоматично вставляється в посилання, згенеровані пагінатором.
У цьому прикладі єдиним аргументом, переданим методу paginate, є кількість елементів, які ви хочете відобразити "на сторінку". У цьому випадку вкажемо, що ми хочемо відобразити 15 елементів на сторінку:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\DB; use Illuminate\View\View; class UserController extends Controller { /** * Показати всіх користувачів застосунку. */ public function index(): View { return view('user.index', [ 'users' => DB::table('users')->paginate(15) ]); } }
Проста пагінація
Метод paginate рахує загальну кількість записів, що відповідають запиту, перед тим як отримати записи з бази даних. Це робиться для того, щоб пагінатор знав, скільки всього сторінок записів є. Однак, якщо ви не плануєте показувати загальну кількість сторінок в інтерфейсі вашого застосунку, тоді запит на підрахунок записів є непотрібним.
Отже, якщо вам потрібно лише відобразити прості посилання "Наступна" і "Попередня" в інтерфейсі вашого застосунку, ви можете використовувати метод simplePaginate для виконання одного ефективного запиту:
$users = DB::table('users')->simplePaginate(15);
Пагінація результатів Eloquent
Ви також можете здійснювати пагінацію запитів Eloquent. У цьому прикладі ми будемо здійснювати пагінацію моделі App\Models\User і вкажемо, що плануємо відображати 15 записів на сторінку. Як ви можете бачити, синтаксис майже ідентичний пагінації результатів побудовника запитів:
use App\Models\User;
$users = User::paginate(15);
Звичайно, ви можете викликати метод paginate після встановлення інших обмежень на запит, таких як where умови:
$users = User::where('votes', '>', 100)->paginate(15);
Ви також можете використовувати метод simplePaginate при пагінації моделей Eloquent:
$users = User::where('votes', '>', 100)->simplePaginate(15);
Аналогічно, ви можете використовувати метод cursorPaginate для курсорної пагінації моделей Eloquent:
$users = User::where('votes', '>', 100)->cursorPaginate(15);
Кілька екземплярів пагінатора на сторінці
Іноді вам може знадобитися відобразити два окремі пагінатори на одному екрані, який відображається вашим застосунком. Однак, якщо обидва екземпляри пагінатора використовують параметр рядка запиту page для зберігання поточної сторінки, два пагінатори будуть конфліктувати. Щоб вирішити цей конфлікт, ви можете передати ім'я параметра рядка запиту, який ви бажаєте використовувати для зберігання поточної сторінки пагінатора, через третій аргумент, наданий методам paginate, simplePaginate і cursorPaginate:
use App\Models\User;
$users = User::where('votes', '>', 100)->paginate(
$perPage = 15, $columns = ['*'], $pageName = 'users'
);
Cursor Пагінація
Хоча paginate і simplePaginate створюють запити, використовуючи SQL-оператор "offset", курсорна пагінація працює шляхом побудови "where" умов, які порівнюють значення впорядкованих стовпців, що містяться в запиті, забезпечуючи найефективнішу продуктивність бази даних серед усіх методів пагінації Laravel. Цей метод пагінації особливо добре підходить для великих наборів даних і "нескінченних" інтерфейсів прокрутки.
На відміну від пагінації на основі зсуву, яка включає номер сторінки в рядок запиту URL-адрес, згенерованих пагінатором, пагінація на основі курсора розміщує рядок "курсор" у рядку запиту. Курсор є закодованим рядком, що містить місце, з якого наступний запит на пагінацію повинен почати пагінацію, і напрямок, у якому вона повинна виконуватися:
http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0
Ви можете створити екземпляр пагінатора на основі курсора за допомогою методу cursorPaginate, який пропонує конструктор запитів. Цей метод повертає екземпляр Illuminate\Pagination\CursorPaginator:
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
Як тільки ви отримали екземпляр курсорного пагінатора, ви можете відобразити результати пагінації, як зазвичай, використовуючи методи paginate та simplePaginate. Для отримання додаткової інформації про методи екземпляра, які пропонує курсорний пагінатор, будь ласка, зверніться до документації методів екземпляра курсорного пагінатора.
Ваш запит повинен містити клаузу "order by", щоб скористатися пагінацією курсора. Крім того, стовпці, за якими впорядковується запит, повинні належати таблиці, яку ви пагінуєте.
"Cursor" проти "Offset" Пагінації
Щоб проілюструвати відмінності між пагінацією зі зсувом та пагінацією з курсором, давайте розглянемо деякі приклади SQL-запитів. Обидва наступні запити відобразять "другу сторінку" результатів для таблиці users, впорядкованої за id:
# Пагінація зі зсувом... select * from users order by id asc limit 15 offset 15; # Пагінація Cursor... select * from users where id > 15 order by id asc limit 15;
Запит пагінації курсором пропонує наступні переваги над пагінацією з використанням offset:
- Для великих наборів даних пагінація курсором забезпечить кращу продуктивність, якщо стовпці "order by" індексовані. Це тому, що клаузула "offset" сканує всі раніше знайдені дані.
- Для наборів даних з частими записами, пагінація зі зміщенням може пропустити записи або показати дублікати, якщо результати нещодавно були додані або видалені зі сторінки, яку користувач наразі переглядає.
Однак, курсорна пагінація має такі обмеження:
- Як і
simplePaginate, курсорна пагінація може використовуватися лише для відображення посилань "Наступна" та "Попередня" і не підтримує генерацію посилань з номерами сторінок. - Це вимагає, щоб сортування базувалося на принаймні одному унікальному стовпці або комбінації стовпців, які є унікальними. Стовпці з
nullзначеннями не підтримуються. - Вирази запитів у клаузах "order by" підтримуються лише, якщо вони мають псевдоніми та додані до клаузи "select".
- Запити з параметрами не підтримуються.
Ручне створення пагінатора
Іноді ви можете захотіти створити екземпляр пагінації вручну, передаючи йому масив елементів, які вже є у пам'яті. Ви можете зробити це, створивши екземпляр Illuminate\Pagination\Paginator, Illuminate\Pagination\LengthAwarePaginator або Illuminate\Pagination\CursorPaginator, залежно від ваших потреб.
Класи Paginator та CursorPaginator не потребують знати загальну кількість елементів у наборі результатів; однак через це ці класи не мають методів для отримання індексу останньої сторінки. LengthAwarePaginator приймає майже ті самі аргументи, що й Paginator; однак він вимагає підрахунку загальної кількості елементів у наборі результатів.
Іншими словами, Paginator відповідає методу simplePaginate у конструкторі запитів, CursorPaginator відповідає методу cursorPaginate, а LengthAwarePaginator відповідає методу paginate.
Коли ви вручну створюєте екземпляр пагінатора, вам слід вручну "нарізати" масив результатів, які ви передаєте пагінатору. Якщо ви не знаєте, як це зробити, перегляньте функцію PHP array_slice.
Налаштування URL-адрес пагінації
За замовчуванням, посилання, згенеровані пагінатором, відповідатимуть URI поточного запиту. Однак метод пагінатора withPath дозволяє налаштувати URI, який використовується пагінатором при генерації посилань. Наприклад, якщо ви хочете, щоб пагінатор генерував посилання на кшталт http://example.com/admin/users?page=N, ви повинні передати /admin/users до методу withPath:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->withPath('/admin/users');
// ...
});
Додавання значень рядка запиту
Ви можете додати до рядка запиту посилань пагінації, використовуючи метод appends. Наприклад, щоб додати sort=votes до кожного посилання пагінації, ви повинні зробити наступний виклик до appends:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->appends(['sort' => 'votes']);
// ...
});
Ви можете використовувати метод withQueryString, якщо хочете додати всі значення рядка запиту поточного запиту до посилань пагінації:
$users = User::paginate(15)->withQueryString();
Додавання Хеш-Фрагментів
Якщо вам потрібно додати "хеш-фрагмент" до URL-адрес, згенерованих пагінатором, ви можете використовувати метод fragment. Наприклад, щоб додати #users до кінця кожного посилання пагінації, ви повинні викликати метод fragment таким чином:
$users = User::paginate(15)->fragment('users');
Відображення результатів пагінації
Коли ви викликаєте метод paginate, ви отримаєте екземпляр Illuminate\Pagination\LengthAwarePaginator, тоді як виклик методу simplePaginate повертає екземпляр Illuminate\Pagination\Paginator. І, нарешті, виклик методу cursorPaginate повертає екземпляр Illuminate\Pagination\CursorPaginator.
Ці об'єкти надають кілька методів, які описують набір результатів. На додаток до цих допоміжних методів, екземпляри пагінатора є ітераторами і можуть бути перебрані як масив. Отже, як тільки ви отримали результати, ви можете відобразити результати і відобразити посилання на сторінки за допомогою Blade:
<div class="container">
@foreach ($users as $user)
{{ $user->name }}
@endforeach
</div>
{{ $users->links() }}
Метод links відобразить посилання на інші сторінки в наборі результатів. Кожне з цих посилань вже міститиме відповідну змінну рядка запиту page. Пам'ятайте, що HTML, згенерований методом links, сумісний з фреймворком Tailwind CSS.
Налаштування вікна посилань пагінації
Коли пагінатор відображає посилання на сторінки, номер поточної сторінки відображається разом із посиланнями на три сторінки перед і після поточної сторінки. Використовуючи метод onEachSide, ви можете контролювати, скільки додаткових посилань відображається з кожного боку поточної сторінки в межах середнього, ковзного вікна посилань, згенерованого пагінатором:
{{ $users->onEachSide(5)->links() }}
Перетворення результатів у JSON
Класи пагінатора Laravel реалізують контракт інтерфейсу Illuminate\Contracts\Support\Jsonable і надають метод toJson, тому дуже легко конвертувати результати пагінації в JSON. Ви також можете конвертувати екземпляр пагінатора в JSON, повернувши його з маршруту або дії контролера:
use App\Models\User;
Route::get('/users', function () {
return User::paginate();
});
JSON від пагінатора буде включати метаінформацію, таку як total, current_page, last_page та інше. Результати записів доступні через ключ data у JSON масиві. Ось приклад JSON, створеного шляхом повернення екземпляра пагінатора з маршруту:
{ "total": 50, "per_page": 15, "current_page": 1, "last_page": 4, "current_page_url": "http://laravel.app?page=1", "first_page_url": "http://laravel.app?page=1", "last_page_url": "http://laravel.app?page=4", "next_page_url": "http://laravel.app?page=2", "prev_page_url": null, "path": "http://laravel.app", "from": 1, "to": 15, "data":[ { // Запис... }, { // Запис... } ] }
Налаштування Представлення Пагінації
За замовчуванням, представлення, що відображають посилання пагінації, сумісні з фреймворком Tailwind CSS. Однак, якщо ви не використовуєте Tailwind, ви можете визначити власні представлення для відображення цих посилань. Викликаючи метод links на екземплярі пагінатора, ви можете передати ім'я представлення як перший аргумент методу:
{{ $paginator->links('view.name') }} <!-- Передача додаткових даних до представлення... --> {{ $paginator->links('view.name', ['foo' => 'bar']) }}
Однак найпростіший спосіб налаштувати представлення пагінації - це експортувати їх до вашого каталогу resources/views/vendor за допомогою команди vendor:publish:
php artisan vendor:publish --tag=laravel-pagination
Ця команда розмістить представлення у вашому застосунку в директорії resources/views/vendor/pagination. Файл tailwind.blade.php у цій директорії відповідає за представлення пагінації за замовчуванням. Ви можете відредагувати цей файл, щоб змінити HTML пагінації.
Якщо ви хочете призначити інший файл як представлення пагінації за замовчуванням, ви можете викликати методи defaultView та defaultSimpleView пагінатора в межах методу boot вашого класу App\Providers\AppServiceProvider:
<?php namespace App\Providers; use Illuminate\Pagination\Paginator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Paginator::defaultView('view-name'); Paginator::defaultSimpleView('view-name'); } }
Використання Bootstrap
Laravel включає представлення пагінації, створені з використанням Bootstrap CSS. Щоб використовувати ці представлення замість стандартних представлень Tailwind, ви можете викликати методи пагінатора useBootstrapFour або useBootstrapFive у методі boot вашого класу App\Providers\AppServiceProvider:
use Illuminate\Pagination\Paginator; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Paginator::useBootstrapFive(); Paginator::useBootstrapFour(); }
Методи екземпляра Paginator / LengthAwarePaginator
Кожен екземпляр пагінатора надає додаткову інформацію про пагінацію за допомогою наступних методів:
| Метод | Опис |
|---|---|
$paginator->count() | Отримати кількість елементів на поточній сторінці. |
$paginator->currentPage() | Отримати номер поточної сторінки. |
$paginator->firstItem() | Отримати номер першого елемента в результатах. |
$paginator->getOptions() | Отримати параметри пінгатора. |
$paginator->getUrlRange($start, $end) | Створити діапазон URL-адрес для пагінації. |
$paginator->hasPages() | Визначити, чи достатньо елементів для розбиття на кілька сторінок. |
$paginator->hasMorePages() | Визначити, чи є ще елементи в сховищі даних. |
$paginator->items() | Отримати елементи для поточної сторінки. |
$paginator->lastItem() | Отримати номер останнього елемента в результатах. |
$paginator->lastPage() | Отримати номер останньої доступної сторінки. (Недоступно при використанні simplePaginate). |
$paginator->nextPageUrl() | Отримати URL для наступної сторінки. |
$paginator->onFirstPage() | Визначити, чи знаходиться пінгатор на першій сторінці. |
$paginator->onLastPage() | Визначити, чи знаходиться пінгатор на останній сторінці. |
$paginator->perPage() | Кількість елементів, які потрібно показати на сторінці. |
$paginator->previousPageUrl() | Отримати URL для попередньої сторінки. |
$paginator->total() | Визначити загальну кількість відповідних елементів у сховищі даних. (Недоступно при використанні simplePaginate). |
$paginator->url($page) | Отримати URL для заданого номера сторінки. |
$paginator->getPageName() | Отримати змінну рядка запиту, яка використовується для збереження сторінки. |
$paginator->setPageName($name) | Встановити змінну рядка запиту, яка використовується для збереження сторінки. |
$paginator->through($callback) | Трансформувати кожен елемент за допомогою зворотного виклику. |
Методи екземпляра Cursor Paginator
Кожен екземпляр курсорного пагінатора надає додаткову інформацію про пагінацію за допомогою наступних методів:
| Метод | Опис |
|---|---|
$paginator->count() | Отримати кількість елементів на поточній сторінці. |
$paginator->cursor() | Отримати поточний екземпляр курсора. |
$paginator->getOptions() | Отримати параметри пінгатора. |
$paginator->hasPages() | Визначити, чи достатньо елементів для розбиття на кілька сторінок. |
$paginator->hasMorePages() | Визначити, чи є ще елементи в сховищі даних. |
$paginator->getCursorName() | Отримати змінну рядка запиту, яка використовується для збереження курсора. |
$paginator->items() | Отримати елементи для поточної сторінки. |
$paginator->nextCursor() | Отримати екземпляр курсора для наступного набору елементів. |
$paginator->nextPageUrl() | Отримати URL для наступної сторінки. |
$paginator->onFirstPage() | Визначити, чи знаходиться пінгатор на першій сторінці. |
$paginator->onLastPage() | Визначити, чи знаходиться пінгатор на останній сторінці. |
$paginator->perPage() | Кількість елементів, які потрібно показати на сторінці. |
$paginator->previousCursor() | Отримати екземпляр курсора для попереднього набору елементів. |
$paginator->previousPageUrl() | Отримати URL для попередньої сторінки. |
$paginator->setCursorName() | Встановити змінну рядка запиту, яка використовується для збереження курсора. |
$paginator->url($cursor) | Отримати URL для заданого екземпляра курсора. |
