こんちわ。ゆたんぽです。
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側でページネーションを実装できます。
表示する件数は適宜変更してください。
何かのヒントになれば嬉しいにゃ