Laravel + Vue でページリンクをつくる(ダウンロード可)

さてさて、前回初心に返ってVueの基本、バインディングについてまとめた記事 Vueでフォーム・バインディングする全実例!(ダウンロード可)をお届けしました。

その流れと言っては何ですが、今回記事として紹介したいと考えていた内容を思い出しました。それはLaravel + Vueでページ(パジネーション)リンクをつくることです。

というのも、最近のウェブサイト開発ではAjaxでデータを取得してVueやReactでリストを描画することが昔よりも一般的になってきているからです。

ということで、今回も順を追って手順を紹介し、さらにソースコード一式をダウンロードできるようにしましたので、ぜひウェブ開発の学習に役立ててくださいね。

では実際に開発をしていきましょう!

やりたいこと

今回は、Ajaxを通してデータ取得し、そのデータを元にJavaScript(Vue.js)を使って以下のようなページリンクを作成します。

テストデータを用意する

まず、Ajaxで取得するテストデータを用意するために、以下のコマンドでモデルとマイグレーションを作成します。(-mをつけるとマイグレーションも一緒に作成できます)

php artisan make:model Item -m

そして、作成されたdatabase/migrations/****_**_**_******_create_items_table.phpというファイルを開き、nameフィールドを追加してマイグレーションを実行します。

public function up()
{
    Schema::create('items', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}
php artisan migrate

マイグレーションを実行するとテーブルはこうなります。

では、ここにテストデータを追加するために以下のコマンドでItemsTableSeederを作成します。

php artisan make:seed ItemsTableSeeder

database/seeds/ItemsTableSeeder.phpが作成されているので、そのファイルを開いて以下のようにテストデータをitemsテーブルへ追加するコードを書き込みます。

<?php

use Illuminate\Database\Seeder;

class ItemsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        for($i = 1 ; $i <= 100 ; $i++) {

            \App\Item::create([
                'name' => $i .'番目の商品名'
            ]);

        }
    }
}

さらに、以下のようにdatabase/seeds/DatabaseSeeder.phpItemsTableSeederを登録してSeederを実行します。

public function run()
{
     $this->call(ItemsTableSeeder::class);
}
php artisan migrate:fresh --seed

※ 他のSeederも存在しているためこのコマンドを使っていますが、そうでない場合はphp artisan db:seedでも問題ありません。

実行が完了するとテーブルはこうなります。

Ajaxでデータを取得する

では、ここからAjaxを通してデータ取得する部分を開発していきます。

Routeを追加する

今回の機能に必要となるのは、ページリンクを表示するメインページとAjaxでデータを取得するページ2つなので以下のように設定します。

Route::get('pagination', 'PaginationController@index'); // メイン
Route::get('ajax/pagination', 'Ajax\PaginationController@index'); // Ajax

まだ必要となる2つのコントローラーが作成できていませんので、以下のコマンドで作成します。

php artisan make:controller PaginationController
php artisan make:controller Ajax\\PaginationController

メインページを作成する

そして、メインページのコントローラーにindex()を作成し、この中でpaginationという名前のビューを読み込む記述をしましょう。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PaginationController extends Controller
{
    public function index() {

        return view('pagination');

    }
}

もちろんpaginationビューはまだ存在していないので、resources/views/pagination.blade.phpを作成して、まずはVueaxios(Ajax通信するJSライブラリ)、そして見た目を綺麗にしたいのでbootstrap(cssのみ)が使えるように以下の内容を保存します。

<html>
<head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div id="app">

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
    <script>

        new Vue({
            el: '#app'
        });

    </script>
</body>
</html>

Ajax部分を作成する

次に、Ajax用のコントローラーにindex()を追加して以下の内容を追加します。

<?php

namespace App\Http\Controllers\Ajax;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PaginationController extends Controller
{
    public function index() {

        return \App\Item::paginate(25);

    }
}

paginate(***)を使うと、指定した件数でページごとのデータ取得ができるだけでなく、

  • current_page ・・・ 現在のページ番号
  • first_page ・・・ 最初のページ番号
  • last_page ・・・ 最後のページ番号

など、ページリンクに必要なデータを自動で用意してくれます。

では、次にJavaScript部分です。

new Vue({
    el: '#app',
    data: {
        page: 1,
        items: []
    },
    methods: {
        getItems() {

            const url = '/ajax/pagination?page='+ this.page;
            axios.get(url)
                .then((response) => {

                    this.items = response.data;

                });

        }
    },
    mounted() {

        this.getItems();

    }
});

まずdataに以下の変数を追加し、

  • page ・・・ 現在のページ番号
  • items ・・・ itemsテーブルのページごとのデータ(付随データ含む)

Ajaxを通してデータを取得するgetItems()メソッドを追加。さらに、ページが表示されたらすぐデータを取得するためmounted()内でgetItems()を呼ぶようにします。

では、実際にブラウザで開いて通信内容をチェックしてみましょう。

うまくいきました!
では次からは本題のページリンクを作成していきます。

ページリンクをつくる

まずページリンクの部分ですが、今回は独自のVueコンポーネントを作成します。
これは、ページリンクを利用する頻度は高いので、いつでも使いまわしができる(=つまり開発効率を上げる)ためです。

Vueコンポーネントをつくる

そのため、まずはpublic/js/vue/v-pagination.jsというファイルをつくり、ここにコンポーネントを作成していきます。

実際のコードは以下になります。

Vue.component('v-pagination', {
    props: {
        data: {}  // paginate()で取得したデータ
    },
    methods: {
        move(page) {

            if(!this.isCurrentPage(page)) {

                this.$emit('move-page', page);

            }

        },
        isCurrentPage(page) {

            return (this.data.current_page == page);

        },
        getPageClass(page) {

            let classes = ['page-item'];

            if(this.isCurrentPage(page)) {

                classes.push('active');

            }

            return classes;

        }
    },
    computed: {
        hasPrev() {

            return (this.data.prev_page_url != null);

        },
        hasNext() {

            return (this.data.next_page_url != null);

        },
        pages() {

            let pages = [];

            for(let i = 1 ; i <= this.data.last_page ; i++) {

                pages.push(i);

            }

            return pages;

        }
    },
    template:
        '<ul class="pagination">'+
            '<li class="page-item" v-if="hasPrev">'+
                '<a class="page-link" href="#" @click.prevent="move(data.current_page-1)">前へ</a>'+
            '</li>'+
            '<li :class="getPageClass(page)" v-for="page in pages">'+
                '<a class="page-link" href="#" v-text="page" @click.prevent="move(page)"></a>'+
            '</li>'+
            '<li class="page-item" v-if="hasNext">'+
                '<a class="page-link" href="#" @click.prevent="move(data.current_page+1)">次へ</a>'+
            '</li>'+
        '</ul>'
});

少しコードが長くなったので、重要な部分をひとつずつ説明していきます。

props

dataは、Ajax(つまり、Laravelのpaginate())で取得したデータで、このデータを元にリンクの表示非表示を決めることになります。

hasPrev と hasNext

「前へ」「次へ」を表示するかどうかを判断する疑似変数(computed)です。もしtrueが返ってくるなら該当するリンクを表示するようになっています。

pages

pagesの内容は、例えば1〜5ページまである場合は1, 2, 3, 4, 5の配列が返ってくるようになっています。そして、この配列をv-forでループさせてページ番号が書かれたリンクを作るようになっています。

getPageClass(page)

以下のように現在のページを青く表示するためには、bootstrap 4 ではclassactiveを追加する必要があります。そのため、このメソッド内で現在ページだけにactiveを追加しています。

move(page)

ページリンクがクリックされた時に実行するメソッドです。内部ではmove-pageという独自のイベントを呼び出し側に通知しています。

コンポーネントを設置する

そのため、このコンポーネントを呼び出しているタグ内では以下のようなイベントを設定しておく必要があります。

<v-pagination :data="items" @move-page="movePage($event)"></v-pagination>

そして、movePage()メソッドの中では、現在のページ番号を変更し、再度Ajax通信でデータを取得するようになっています。

movePage(page) {

    this.page = page; // ページ番号を更新
    this.getItems(); // Ajaxで新データを取得

}

ちなみに@click.preventとしているのは、以下のようなリンクをクリックしてしまうとスクロール位置が変わる可能性があるためです。

<a href="#" @click="****()">***</a>

これでページリンクが作成できました。

ページリンクをテストしてみる

では、以下のリンクをクリックしてテストしてみましょう。

まずは「次へ」リンクをクリックしてみます。

表示されていなかった「前へ」リンクが表示され、現在ページは「2」になりました。
では、直接最後の「4」をクリックしてみましょう。

現在ページが「4」になり、「次へ」リンクが消えました。

※ ちなみに今回はテストなので省略していますが、データが多くなってくるとリンク数が長くなってしまいます。その場合はv-paginationpages()内をカスタマイズし、表示したい最大ページ数を変更するといいでしょう。

おまけ

せっかくデータ取得しているので、リストで表示してページ移動が本当にうまくいっているかもチェックしてみましょう。

といっても、Ajaxで取得したデータをv-forでループさせながら表示するだけです。

<ul class="list-group">
    <li class="list-group-item" v-for="item in items.data" v-text="item.name"></li>
</ul>

では、表示してみます。

25番目までの商品名が表示されました。
次のページに進みます。

ページが2になり、25〜50番目の商品が表示されました。
全て完了です。

今回使ったコードをダウンロードする

今回の説明で作成したソースコード一式を以下からダウンロードすることができます。

※ ただし、DatabaseSeederなどは注意しながら上書きしてください。

Laravel + Vue でページリンクをつくるソースコード