こんちわ。ゆたんぽです。


Laravelにはデフォルトでページネーション機能がついており、コントローラーなどでデータを取得して渡す際に、「paginate(10)」 のようにするだけで自動でページネーションを実装してくれるので大変便利ですよね。

ただ、Vue.js + Laravel を使って開発をするにあたり

① フロント側はVue.jsで作る(Laravelのテンプレート(View)は使わない)
② APIで、Laravelで処理したデータを取得して表示する


といった必要がある場合、テンプレートでなら問題なく表示できる「paginate()」が使えなくなってしまいます・・・。

このあたりを解決するために、今回Laravelさんにはデータの取得や処理の方をお願いし、
それをAPI通信(今回はaxiosを使用)で、Vue側に持ってきて、ページネーション「風」のものを実装する手順を紹介します。

この記事でなにをやってるかを簡単に言うと

「Vue側からaxiosを使って、Laravel側にアクセスしてデータを取得。そのデータをjsでページネーション風に表示しちゃえ。」

ということです。何かの参考になれば幸いです。

前提として

当方の動作環境(package.json内容)

・ Laravel:5.8
・Vue.js:2.5.17
・axios: 0.19.2

サンプルとして以下を用意

1. TestController.php・・・データ取得用、API通信用のメソッドを記載したコントローラー
2. ExampleComponent.vue・・・1からAPI通信でデータを取得し、表示するためのVueコンポーネント

※今回はページネーションが目的のため、「API用のルートの設定方法」や「Vueコンポーネントの作り方」などなど。

細かい部分の設定方法について等は割愛します。後日、そういった部分についての記事も出す予定です。

DB、データ、モデルについて

・すでに DBに「reviews」テーブルが用意されており、それぞれカラムなども揃っている。
・Reviewモデルも作成済み。リレーションなども設定されている。という前提で進めます。

作り方(Laravel側)

まずは Laravel側、「TestController.php」から作っていきます。
ここでは主に2つのメソッドを作ることとします。

 ① DBにあるデータの取得用メソッドの作成

まずは DBからデータを必要な分、取得するためのメソッド()を作成します。

【TestController.php】

// Reviewモデルをuseするのも忘れずに。
class TestController extends Controller{
    
    public function reviews()  {  // ★①
       
        $reviews = Review::all();    // レビューモデルから今回はデータを全件取得。
        $data = [
            'reviews' => $reviews,     // Vue側に渡す$data(中には'reviews'が入ってる)を定義
        ];
    
        return response()->json($data);    // 今回はjson形式でVue側に渡します。
    }

これで、Vueコンポーネントに「$data」がjson形式で渡される準備ができました。

 ② ルートの設定

API通信を行うためのルートが必要になります。
今回はGET通信で「/api/reviews」というURLにアクセスがあった場合、①のメソッドを呼び出す形にします。

【routes/web.php(など)】

// APIのミドルウェアを使用
Route::middleware('api')->group(function() {
    // レビューの取得
    Route::get('/api/reviews', 'TestController@reviews');
}
ゆたんぽ

とりあえず、Laravel側はこんな感じかな。

ぬこ先生

ルートはAPI用にファイル分けて管理しても良いかもにゃ

作り方(Vueコンポーネント側)

次はVue側で

・Laravel側からAPI通信(axios)でデータを受け取る
・そのデータを表示する
・ページネーションによって表示するデータの内容を変える

ということをしていきます。

 ① データを表示するためのコードを記述する

【ExampleComponent.vue】
に、例えば以下のような形で、取得したレビューを表示する処理を記述していきます。

<template>
   <div>
        <div v-for="review in paginatedReviews" :key="review.id"> 
          
            <div>
              <h3>{{ review.title }}</h3> // レビュータイトル
              <p>{{ formatDate(review.created_at) }}</p> // レビューの投稿日
              <p>{{ review.score }}</p> // 評価
              <p>{{ review.description }}</p> // レビュー内容
            </div>
         </div>

 ② ページネーションを表示するためのコードを記述

さらに追加して、ページネーションの表示部分を追記していきます。

<!-- ページネーションの追加 -->
    <div>
      <button v-if="currentPage > 1" @click="changePage(1)">
        <
      </button>
      <!-- 前のページボタン -->
      <button v-if="currentPage > 2" @click="changePage(currentPage - 1)">
        prev
      </button>
      <!-- 動的に変化するページボタン -->
      <button v-for="pageNumber in visiblePageNumbers" :key="pageNumber" :class="{ active: currentPage === pageNumber }" @click="changePage(pageNumber)">
        {{ pageNumber }}
      </button>
      <!-- 次のページボタン -->
      <button v-if="currentPage < totalPages - 1" @click="changePage(currentPage + 1)">
        next
      </button>
      <!-- 最終ページ -->
      <button v-if="currentPage < totalPages" @click="changePage(totalPages)">
        >
      </button>
    </div>
  </div>
</template>

 ③ ページネーションの処理を記述

<script>
import axios from 'axios'; // axiosを読み込み
export default {
  data() {
    return {
      reviews: [],
      currentPage: 1,
      reviewsPerPage: 6, // 1ページに表示するレビューの数
    };
  },
  async mounted() {
    await this.getReviews(); // ページが読み込まれたときにレビューをAPIで取得
  },
  computed: {
 
 // 全部で何ページになるか
    totalPages() {
      return Math.ceil(this.reviews.length / this.reviewsPerPage);
    },
    startIndex() {
      return (this.currentPage - 1) * this.reviewsPerPage;
    },
    endIndex() {
      return this.startIndex + this.reviewsPerPage;
    },
 // ページ毎に表示するレビューを計算し変更
    paginatedReviews() {
      return this.reviews.slice(this.startIndex, this.endIndex);
    },
    visiblePageNumbers() {
      const totalPages = this.totalPages;
      const currentPage = this.currentPage;
      const maxVisiblePages = 3;
      if (totalPages <= maxVisiblePages) {
        return Array.from({ length: totalPages }, (_, i) => i + 1);
      } else {
        const middlePage = Math.floor(maxVisiblePages / 2) + 1;
        if (currentPage <= middlePage) {
          return Array.from({ length: maxVisiblePages }, (_, i) => i + 1);
        } else if (currentPage >= totalPages - middlePage + 1) {
          return Array.from({ length: maxVisiblePages }, (_, i) => totalPages - maxVisiblePages + i + 1);
        } else {
          return Array.from({ length: maxVisiblePages }, (_, i) => currentPage - middlePage + i);
        }
      }
    },
  },
  methods: {
    // APIでレビューを取得するメソッド
    getReviews() {
      axios
        .get('/api/reviews')
        .then((response) => {
          this.reviews = response.data.reviews;
        })
        .catch((error) => {
          console.error(error);
        });
    },
    // ページを変更するメソッド
    changePage(pageNumber) {
      this.currentPage = pageNumber;
    },
  },
  
};
</script>

このようにすることで、Laravel側からAPI経由でデータを取得して、Vue側でページネーションを実装できます。

ゆたんぽ

表示する件数は適宜変更してください。

ぬこ先生

何かのヒントになれば嬉しいにゃ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です