Постраничный вывод результатов в Eloquent

Eloquent – это объектно-реляционный преобразователь (ORM), который включен в структуру Laravel по умолчанию. В этой серии статей вы узнаете, как делать запросы к базе данных и как работать с отношениями в Laravel Eloquent. Также вы сможете попрактиковаться на примерах: мы попробуем улучшить демо-приложение с помощью новых моделей и отношений.

Примечание: Все мануалы данной серии можно найти по тегу Laravel Eloquent.

В ходе выполнения этой серии статей мы добавляли в демо-приложение новые ссылки, чтобы протестировать несколько функций Laravel Eloquent. Возможно, вы заметили, что после добавления каждой новой ссылки главная страница становится длиннее. Это происходит потому, что на количество ссылок, отображаемых в приложении, нет ограничений. Это нормально, если в вашей БД небольшое количество записей, но в долгосрочной перспективе это может привести к увеличению времени загрузки страницы. Кроме того, макет будет труднее читать из-за большого количества контента, разбросанного в рамках одной страницы.

В этом мануале вы узнаете, как ограничить количество результатов в запросе Laravel Eloquent с помощью метода limit() и как разбить результаты на страницы с помощью метода simplePaginate().

Ограничение результатов запроса

Для начала обновите свой основной маршрут приложения (/), чтобы ограничить количество ссылок, перечисленных на вашей главной странице.

Откройте файл web.php в редакторе кода:

routes/web.php

Затем найдите в нем определение основного маршрута:

Route::get('/', function () {
    $links = Link::all()->sortDesc();
    return view('index', [
        'links' => $links,
        'lists' => LinkList::all()
    ]);
});

Выделенная строка показывает запрос, который в настоящее время получает все ссылки в БД с помощью метода all() модели Link. Как объяснялось в предыдущей части этой серии, данный метод наследуется от родительского класса Model и возвращает коллекцию со всеми записями БД, связанными с этой моделью. Метод sortDesc() используется для сортировки результирующей коллекции в порядке убывания.

Мы изменим выделенную строку, чтобы использовать метод сортировки запросов orderBy(). Он упорядочивает результаты запроса на уровне базы данных вместо того, чтобы просто переупорядочить полный набор строк, который возвращается методом all() как Eloquent Collection. Также мы включим вызов метода limit(), чтобы ограничить результаты запроса, и воспользуемся методом get(), чтобы получить отфильтрованный набор результатов в виде Eloquent Collection.

Замените свой основной маршрут следующей строкой (отредактированная строка выделена для вашего удобства):

Route::get('/', function () {
    $links = Link::orderBy('created_at', 'desc')->limit(4)->get();

    return view('index', [
        'links' => $links,
        'lists' => LinkList::all()
    ]);

});

Наш новый код будет извлекать последние 4 ссылки, добавленные в базу данных (независимо от списка). Поскольку все ссылки добавляются в списки, посетители по-прежнему смогут переходить к определенным спискам, чтобы увидеть полный список ссылок.

Далее мы попробуем разбить результаты на страницы, чтобы убедиться, что все ссылки по-прежнему доступны, даже если они не загружаются на одной странице.

Разбивка результатов запроса на страницы

Теперь количество ссылок на главной странице ограничено, чтобы не перегружать экран контентом и быстрее загружать саму страницу. Во многих случаях это решение работает хорошо, однако вам все еще необходимо убедиться, что посетители по-прежнему могут получать доступ к более старым ссылкам, которые не видны из-за наших новых настроек. Самый эффективный способ сделать это – разбить результат на страницы, чтобы пользователи могли перемещаться между ними.

Laravel Eloquent предлагает встроенные методы для облегчения настройки постраничного вывода. Методы paginate() и simplePaginate() обеспечивают создание ссылок для постраничного вывода и обработку параметров HTTP для определения того, какая страница запрашивается в данный момент. Также они правильно выполняют запросы к базе данных и получают ожидаемый набор результатов (в зависимости от количества записей на странице).

Давайте обновим запросы Eloquent в routes/web.php и добавим метод simplePaginate(), который генерирует базовую навигацию с предыдущими и последующими ссылками. В отличие от метода paginate(), simplePaginate() не показывает информацию об общем количестве страниц в результате запроса.

Откройте файл routes/web.php в редакторе кода. Начните с обновления главного маршрута (/), заменив вызов метода limit(4)->get() на метод simplePaginate():

...
Route::get('/', function () {
    $links = Link::orderBy('created_at', 'desc')->simplePaginate(4);

    return view('index', [
        'links' => $links,
        'lists' => LinkList::all()
    ]);
});
...

Затем найдите в том же файле определение маршрута /{slug} и замените метод get() на simplePaginate(). Вот как должен выглядеть этот фрагмент после того, как вы закончите:

...
Route::get('/{slug}', function ($slug) {
    $list = LinkList::where('slug', $slug)->first();
    if (!$list) {
        abort(404);
    }

    return view('index', [
        'list' => $list,
        'links' => $list->links()->orderBy('created_at', 'desc')->simplePaginate(4),
        'lists' => LinkList::all()
    ]);
})->name('link-list');
...

Вот как будет выглядеть готовый файл routes/web.php, когда вы закончите его редактировать (изменения выделены для удобства):

<?php
use Illuminate\Support\Facades\Route;
use App\Models\Link;
use App\Models\LinkList;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    $links = Link::orderBy('created_at', 'desc')->simplePaginate(4);

    return view('index', [
        'links' => $links,
        'lists' => LinkList::all()
    ]);
});

Route::get('/{slug}', function ($slug) {
    $list = LinkList::where('slug', $slug)->first();
    if (!$list) {
        abort(404);
    }

    return view('index', [
        'list' => $list,
        'links' => $list->links()->orderBy('created_at', 'desc')->simplePaginate(4),
        'lists' => LinkList::all()
    ]);
})->name('link-list');

Сохраните файл.

Запросы к базе данных мы обновили, но нам также нужно обновить представление и включить в него код, который будет отображать панель навигации. Итоговая коллекция Eloquent, полученная с помощью simplePaginate(), содержит метод links(), который можно вызвать из представления для вывода необходимого HTML-кода. Данный код будет отображать раздел навигации на основе запроса Eloquent с настроенным постраничным выводом.

Также метод links() можно использовать в разбитой на страницы коллекции Eloquent для доступа к объекту paginator, который предоставляет несколько полезных методов для получения информации (например, информации о текущей странице и о том, разбит ли результат на несколько страниц или нет).

Откройте представление приложения resources/views/index.blade.php в редакторе кода:

resources/views/index.blade.php

Найдите конец раздела links, который содержит цикл foreach, где отображаются ссылки. Поместите следующий фрагмент кода после этого раздела и перед последним тегом </div>:

@if ($links->links()->paginator->hasPages())
    <div class="mt-4 p-4 box has-text-centered">
        {{ $links->links() }}
    </div>
@endif

Этот код проверяет наличие нескольких страниц результатов, обращаясь к объекту paginator и вызывая метод hasPages(). Когда этот метод возвращает true, страница отображает новый элемент div и вызывает метод links() для отображения ссылок соответствующего запроса Eloquent.

Вот как будет выглядеть обновленная страница index.blade.php, когда вы закончите редактирование:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My Awesome Links</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">

    <style>
        html {
            background: url("https://i.imgur.com/BWIdYTM.jpeg") no-repeat center center fixed;
            -webkit-background-size: cover;
            -moz-background-size: cover;
            -o-background-size: cover;
            background-size: cover;
        }

        div.link h3 {
            font-size: large;
        }

        div.link p {
            font-size: small;
            color: #718096;
        }
    </style>
</head>
<body>
<section class="section">
    <div class="container">
        <h1 class="title">
            @if (isset($list))
                {{ $list->title }}
            @else
                Check out my awesome links
            @endif
        </h1>
        <p class="subtitle">
            @foreach ($lists as $list)<a href="{{ route('link-list', $list->slug) }}" title="{{ $list->title }}" class="tag is-info is-light">{{ $list->title }} ({{ $list->links()->count() }})</a> @endforeach
        </p>

        <section class="links">
            @foreach ($links as $link)
                <div class="box link">
                    <h3><a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a></h3>
                    <p>{{$link->url}}</p>
                    <p class="mt-2"><a href="{{ route('link-list', $link->link_list->slug) }}" title="{{ $link->link_list->title }}" class="tag is-info">{{ $link->link_list->title }}</a></p>
                </div>
            @endforeach
        </section>

        @if ($links->links()->paginator->hasPages())
            <div class="mt-4 p-4 box has-text-centered">
                {{ $links->links() }}
            </div>
        @endif
    </div>
</section>
</body>
</html>

Сохраните файл, когда закончите его редактировать. Если вы вернетесь в окно браузера и обновите страницу приложения, вы увидите новую панель навигации – она будет появляться всякий раз, когда в общем списке или на любой отдельной странице будет более 4 ссылок.

Настроив постраничный вывод результатов, вы можете спокойно добавлять новые записи, расширять контент, не беспокоясь о том, что старые записи потеряются на сайте. Если же вам нужно отображать только фиксированное количество результатов на основе определенных критериев, а разбивка на страницы не нужна, вы можете просто использовать метод limit(), чтобы упростить свой запрос.

Tags: , ,

Добавить комментарий