【Laravel10 Livewire3】Paginationを実装する

Laravel

Laravel10 + Livewire3 でPaginationを実装していきます。

Pagination | Laravel
A full-stack framework for Laravel that takes the pain out of building dynamic UIs.

この記事のゴール

  • Laravel10 + Livewire3の環境でPaginationを実装
  • スタイル適用で見やすくする
  • WEBブラウザでユーザー一覧の表示切替をする

前提条件

  • Ubuntu上で作業しています
  • PHP8.2以降インストール済
  • Composer v2以降インストール済

環境構築(Laravel10 + Livewire3)

作業フォルダ内に新規プロジェクト「pagination-livewire」を作成していきます。

composer create-project laravel/laravel:^10 pagination-livewire

プロジェクトフォルダ内に移動してから、

Livewireをインストールしていきます。

cd pagination-livewire/
composer require livewire/livewire:^3

プロジェクトトップにある「.env」を編集・保存します。

DB_CONNECTION=sqlite

に修正して、それ以外のDB項目を削除します。

「database/seeders/DatabaseSeeder.php」を編集・保存します。

        // \App\Models\User::factory(10)->create();

の箇所を修正します。

        \App\Models\User::factory(150)->create();

シードオプション付きでマイグレーション実行します。

php artisan migrate --seed

ユーザーが150件作成されました。

Livewireコンポーネント作成

次のコマンドでコンポーネントを作成します。

php artisan make:livewire users

筆者はこのアスキーアートでテンションが上がります。

「app/Livewire/Users.php」

「resources/views/livewire/users.blade.php」

の2ファイルが作成されました。

クラスファイル編集

公式の注意書きとして、WithPaginationトレイトを使えとあります。

You must use theWithPagination trait

To take advantage of Livewire’s pagination features, each component containing pagination must use the Livewire\WithPagination trait.

Pagination | Laravel Livewire

自動翻訳:

WithPagination 特性を使用する必要があります
Livewire のページネーション機能を利用するには、ページネーションを含む各コンポーネントで LivewireWithPagination トレイトを使用する必要があります。

「app/Livewire/Users.php」を開いて編集・保存します。

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\WithPagination;
use App\Models\User;

class Users extends Component
{
    use WithPagination;

    public function render()
    {
        return view('livewire.users', [
            'users' => User::paginate(10),
        ]);
    }
}

ビューファイル編集

「resources/views/livewire/users.blade.php」を編集・保存します。

<div>
    <div>
        @isset ($users)
        <ul>
        @foreach ($users as $user)
            <li>{{ $user->id }}: {{ $user->name }} &lt;{{ $user->email }}&gt;</li>
        @endforeach
        </ul>
        @else
        <p>No users.</p>
        @endisset
    </div>
 
    {{ $users->links() }}
</div>

ルーティング

「routes/web.php」に追記します。

Route::get('/users', \App\Livewire\Users::class);

テンプレートレイアウト作成

次のコマンドを実行します。

php artisan livewire:layout

「resources/views/components/layouts/app.blade.php」

が作成されました。

WEBサービス起動

次のコマンドでWEBサービスを起動します。

php artisan serve

ブラウザで確認

WEBブラウザで

http://127.0.0.1:8000/users

にアクセスします。

このような感じで表示されました。

スタイル適用されていないので見づらいですが、

一応機能はしています。

スタイルを適用してみる

Tailwind CSSのCDN版を適用してみます。

「resources/views/components/layouts/app.blade.php」に

<script src="https://cdn.tailwindcss.com"></script>

を追記します。

WEBブラウザを再読み込みしてみます。

スタイルは適用されましたが、ページ番号が消えてしまいました。

Paginationテンプレートを修正してページ番号を表示し、スタイルを調整して見やすくしていきます。

まずは、Livewireで使用しているPaginationテンプレートをpublishします。

php artisan livewire:publish --pagination

「resources/views/vendor/livewire/」フォルダに

4個のテンプレートが展開されました。

デフォルトの「tailwind.blade.php」を同じフォルダに

「tailwind-modified.blade.php」としてコピーし、

編集・保存します。

長いのでやったことを列記します。

  • 「<<Previous」「Next>>」を削除
  • 「Showing …」を削除
  • 現在のページを背景青、テキスト白に変更
  • マウスホバーで背景黄色に変更
@php
if (! isset($scrollTo)) {
    $scrollTo = 'body';
}

$scrollIntoViewJsSnippet = ($scrollTo !== false)
    ? <<<JS
       (\$el.closest('{$scrollTo}') || document.querySelector('{$scrollTo}')).scrollIntoView()
    JS
    : '';
@endphp

<div>
    @if ($paginator->hasPages())
        <nav role="navigation" aria-label="Pagination Navigation" class="flex items-center justify-between">

            <div class="sm:flex-1 sm:flex sm:items-center sm:justify-between">
                <div>
                    <span class="relative z-0 inline-flex rounded-md shadow-sm">
                        <span>
                            {{-- Previous Page Link --}}
                            @if ($paginator->onFirstPage())
                                <span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
                                    <span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5" aria-hidden="true">
                                        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                            <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
                                        </svg>
                                    </span>
                                </span>
                            @else
                                <button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white hover:bg-yellow-400 border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.previous') }}">
                                    <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                        <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
                                    </svg>
                                </button>
                            @endif
                        </span>

                        {{-- Pagination Elements --}}
                        @foreach ($elements as $element)
                            {{-- "Three Dots" Separator --}}
                            @if (is_string($element))
                                <span aria-disabled="true">
                                    <span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5 select-none">{{ $element }}</span>
                                </span>
                            @endif

                            {{-- Array Of Links --}}
                            @if (is_array($element))
                                @foreach ($element as $page => $url)
                                    <span wire:key="paginator-{{ $paginator->getPageName() }}-page{{ $page }}">
                                        @if ($page == $paginator->currentPage())
                                            <span aria-current="page">
                                                <span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-white bg-blue-700 border border-gray-300 cursor-default leading-5 select-none">{{ $page }}</span>
                                            </span>
                                        @else
                                            <button type="button" wire:click="gotoPage({{ $page }}, '{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white hover:bg-yellow-400 border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
                                                {{ $page }}
                                            </button>
                                        @endif
                                    </span>
                                @endforeach
                            @endif
                        @endforeach

                        <span>
                            {{-- Next Page Link --}}
                            @if ($paginator->hasMorePages())
                                <button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white hover:bg-yellow-400 border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.next') }}">
                                    <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                        <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
                                    </svg>
                                </button>
                            @else
                                <span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
                                    <span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5" aria-hidden="true">
                                        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                            <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
                                        </svg>
                                    </span>
                                </span>
                            @endif
                        </span>
                    </span>
                </div>
            </div>
        </nav>
    @endif
</div>

「resources/views/livewire/users.blade.php」

を編集・保存します。

<div>
    {{ $users->onEachSide(1)->links('vendor.livewire.tailwind-modified') }}
    <div>
        @isset ($users)
        <ul class="p-6 divide-y divide-slate-200">
        @foreach ($users as $user)
            <li class="flex px-6 py-2 hover:bg-green-200 rounded-lg">{{ $user->id }}: {{ $user->name }} &lt;{{ $user->email }}&gt;</li>
        @endforeach
        </ul>
        @else
        <p>No users.</p>
        @endisset
    </div>
</div>

ページネーションを最上部に移動、

現在のページの両脇1件ずつを指定、

テンプレート「vendor.livewire.teilwind-modified」を指定、

ユーザー一覧もスタイル適用しました。

WEBブラウザを再読み込みして見ます。

スタイル適用がうまくいっていますね。

ユーザー一覧の方もスタイル適用されています。

今回はここまでです。お疲れさまでした。

続きはこちらです。

コメント

タイトルとURLをコピーしました