現時点では高々100件のデータですが,数万件,数百万件,数億件といったようにデータ件数が多くなったときに全てのデータをまとめて取得することは現実的ではありません.ここではページネーションを利用して一部のデータだけを取得するようにします.まず,次のとおりルートを変更します.具体的には,これまでルートの定義の中でコメントを一覧で取得していましたがこれをコメントアウト(または削除)して,コントローラの index
関数でコメントの一覧を取得するようにします.
routes/api.php (抜粋)
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::get('/comments/{id}', function (string $id) {
return new CommentResource(Comment::findOrFail($id));
});
// Route::get('/comments', function () {
// return new CommentCollection(Comment::get());
// });
Route::get('/comments', [CommentController::class, 'index']) -> name('comments.index');
Route::post('/comments', [CommentController::class, 'store']) -> name('comments.store');
Route::put('/comments/{comment_id}', [CommentController::class, 'update']) -> name('comments.update');
Route::delete('/comments/{comment_id}', [CommentController::class, 'destroy']) -> name('comments.destroy');
次に,コントローラに index
関数を作成します.一覧取得の際には最終更新日時を降順でソートして結果から指定されたページの3件のみを取得するようにしています.
app/Http/Controllers/CommentController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Comment;
use App\Http\Resources\CommentResource;
use App\Http\Resources\CommentCollection;
class CommentController extends Controller
{
public function index()
{
$comments = Comment::orderBy('updated_at','DESC')
->paginate(3);
return new CommentCollection($comments);
}
public function store(Request $request)
{
$comment = new Comment();
$comment->title = $request->title;
$comment->body = $request->body;
$comment->save();
return new CommentResource($comment);
}
public function update(Request $request, $comment_id)
{
$comment = Comment::findOrFail($comment_id);
$comment->title = $request->title;
$comment->body = $request->body;
$comment->save();
return new CommentResource($comment);
}
public function destroy(Request $request, $comment_id)
{
$comment = Comment::findOrFail($comment_id);
$comment->delete();
return response()->json();
}
}
さらに CommentCollection リソースを次のように変更します.
app/Http/Resources/CommentCollection.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class CommentCollection extends ResourceCollection
{
/**
* 適用する「データ」ラッパー
*
* @var string|null
*/
public static $wrap = 'comments';
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
return [
'comments' => $this->collection,
];
// return parent::toArray($request);
}
/**
* Customize the outgoing response for the resource.
*
* @param \Illuminate\Http\Request
* @param \Illuminate\Http\Response
* @return void
*/
public function withResponse($request, $response)
{
$response->header('Charset', 'utf-8');
$response->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}
}
実際に curl
コマンドを用いてコメントの一覧を取得します.ページを指定しなければ更新日時の最も新しい3件が取得されます.同時に,他のページを取得するためのURIも数多く取得できていることも確認できます.その関係でかなり見辛くなっています.
C:\Users\Rinsaka>curl http://192.168.56.101:8000/api/comments/ ⏎ {"comments":[{"id":56,"title":"山口 七夏","body":"5878352 愛媛県廣川市西 区田中町笹田6-8-6 コーポ三宅108号 \/ kumiko.kudo@example.net","updated_at":"2023-11-30T19:57:49.000000Z"},{"id":99,"title":"斉藤 舞","body":"7186306 千葉県宮沢市南区桐山町鈴木9-1-5 \/ yoshida.nanami@example.org","updated_at":"2023-11-30T02:25:44.000000Z"},{"id":8,"title":"高橋 洋介","body":"1981576 秋田県小林市北区山口町野村4-4-1 \/ kudo.akira@example.net","updated_at":"2023-11-29T19:54:07.000000Z"}],"links":{"first":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","last":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","prev":null,"next":"http:\/\/192.168.56.101:8000\/api\/comments?page=2"},"meta":{"current_page":1,"from":1,"last_page":34,"links":[{"url":null,"label":"« Previous","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","label":"1","active":true},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=2","label":"2","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=3","label":"3","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=4","label":"4","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=5","label":"5","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=6","label":"6","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=7","label":"7","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=8","label":"8","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=9","label":"9","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=10","label":"10","active":false},{"url":null,"label":"...","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=33","label":"33","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","label":"34","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=2","label":"Next »","active":false}],"path":"http:\/\/192.168.56.101:8000\/api\/comments","per_page":3,"to":3,"total":100}} C:\Users\Rinsaka>
上で出力された JSON 形式のデータを整形すると次のようになります.これを見ると,"comments"
というキーに最新の3件のコメントが記録され,それ以外にも "links"
と "meta"
をキーに様々な情報が記録されていることがわかります.
整形した1ページ目の結果
{
"comments": [
{
"id": 56,
"title": "山口 七夏",
"body": "5878352 愛媛県廣川市西 区田中町笹田6-8-6 コーポ三宅108号 / kumiko.kudo@example.net",
"updated_at": "2023-11-30T19:57:49.000000Z"
},
{
"id": 99,
"title": "斉藤 舞",
"body": "7186306 千葉県宮沢市南区桐山町鈴木9-1-5 / yoshida.nanami@example.org",
"updated_at": "2023-11-30T02:25:44.000000Z"
},
{
"id": 8,
"title": "高橋 洋介",
"body": "1981576 秋田県小林市北区山口町野村4-4-1 / kudo.akira@example.net",
"updated_at": "2023-11-29T19:54:07.000000Z"
}
],
"links": {
"first": "http://192.168.56.101:8000/api/comments?page=1",
"last": "http://192.168.56.101:8000/api/comments?page=34",
"prev": null,
"next": "http://192.168.56.101:8000/api/comments?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 34,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=1",
"label": "1",
"active": true
},
{
"url": "http://192.168.56.101:8000/api/comments?page=2",
"label": "2",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=3",
"label": "3",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=4",
"label": "4",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=5",
"label": "5",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=6",
"label": "6",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=7",
"label": "7",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=8",
"label": "8",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=9",
"label": "9",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=10",
"label": "10",
"active": false
},
{
"url": null,
"label": "...",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=33",
"label": "33",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=34",
"label": "34",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=2",
"label": "Next »",
"active": false
}
],
"path": "http://192.168.56.101:8000/api/comments",
"per_page": 3,
"to": 3,
"total": 100
}
}
次にリクエストのURIにパラメータ ?page=4
を付与して4ページ目の3件を取得します.
C:\Users\Rinsaka>curl http://192.168.56.101:8000/api/comments/?page=4 ⏎ {"comments":[{"id":63,"title":"杉山 英樹","body":"7291705 福岡県工藤市東 区中津川町山本2-6-10 \/ tsubasa.sato@example.net","updated_at":"2023-11-28T02:33:10.000000Z"},{"id":24,"title":"三宅 直樹","body":"9878173 北海道渡辺市北区加藤町笹田4-7-2 \/ qkondo@example.org","updated_at":"2023-11-28T02:21:52.000000Z"},{"id":82,"title":"山田 治","body":"9102099 奈良県高橋市 南区松本町山田9-6-7 コーポ田中103号 \/ mikako.tsuda@example.com","updated_at":"2023-11-27T22:21:58.000000Z"}],"links":{"first":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","last":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","prev":"http:\/\/192.168.56.101:8000\/api\/comments?page=3","next":"http:\/\/192.168.56.101:8000\/api\/comments?page=5"},"meta":{"current_page":4,"from":10,"last_page":34,"links":[{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=3","label":"« Previous","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","label":"1","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=2","label":"2","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=3","label":"3","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=4","label":"4","active":true},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=5","label":"5","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=6","label":"6","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=7","label":"7","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=8","label":"8","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=9","label":"9","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=10","label":"10","active":false},{"url":null,"label":"...","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=33","label":"33","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","label":"34","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=5","label":"Next »","active":false}],"path":"http:\/\/192.168.56.101:8000\/api\/comments","per_page":3,"to":12,"total":100}} C:\Users\Rinsaka>
最後のページは34ページ目です.パラメータ ?page=34
を付与して一覧で取得します.
C:\Users\Rinsaka>curl http://192.168.56.101:8000/api/comments/?page=34 ⏎ {"comments":[{"id":1,"title":"最初のコメント","body":"最初のコメントです!","updated_at":"2023-10-02T10:10:10.000000Z"}],"links":{"first":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","last":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","prev":"http:\/\/192.168.56.101:8000\/api\/comments?page=33","next":null},"meta":{"current_page":34,"from":100,"last_page":34,"links":[{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=33","label":"« Previous","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=1","label":"1","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=2","label":"2","active":false},{"url":null,"label":"...","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=25","label":"25","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=26","label":"26","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=27","label":"27","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=28","label":"28","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=29","label":"29","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=30","label":"30","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=31","label":"31","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=32","label":"32","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=33","label":"33","active":false},{"url":"http:\/\/192.168.56.101:8000\/api\/comments?page=34","label":"34","active":true},{"url":null,"label":"Next »","active":false}],"path":"http:\/\/192.168.56.101:8000\/api\/comments","per_page":3,"to":100,"total":100}} C:\Users\Rinsaka>
4ページ目と最終ページの結果も整形したものを示しておきます.
整形した4ページ目の結果
{
"comments": [
{
"id": 63,
"title": "杉山 英樹",
"body": "7291705 福岡県工藤市東 区中津川町山本2-6-10 / tsubasa.sato@example.net",
"updated_at": "2023-11-28T02:33:10.000000Z"
},
{
"id": 24,
"title": "三宅 直樹",
"body": "9878173 北海道渡辺市北区加藤町笹田4-7-2 / qkondo@example.org",
"updated_at": "2023-11-28T02:21:52.000000Z"
},
{
"id": 82,
"title": "山田 治",
"body": "9102099 奈良県高橋市 南区松本町山田9-6-7 コーポ田中103号 / mikako.tsuda@example.com",
"updated_at": "2023-11-27T22:21:58.000000Z"
}
],
"links": {
"first": "http://192.168.56.101:8000/api/comments?page=1",
"last": "http://192.168.56.101:8000/api/comments?page=34",
"prev": "http://192.168.56.101:8000/api/comments?page=3",
"next": "http://192.168.56.101:8000/api/comments?page=5"
},
"meta": {
"current_page": 4,
"from": 10,
"last_page": 34,
"links": [
{
"url": "http://192.168.56.101:8000/api/comments?page=3",
"label": "« Previous",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=1",
"label": "1",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=2",
"label": "2",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=3",
"label": "3",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=4",
"label": "4",
"active": true
},
{
"url": "http://192.168.56.101:8000/api/comments?page=5",
"label": "5",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=6",
"label": "6",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=7",
"label": "7",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=8",
"label": "8",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=9",
"label": "9",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=10",
"label": "10",
"active": false
},
{
"url": null,
"label": "...",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=33",
"label": "33",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=34",
"label": "34",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=5",
"label": "Next »",
"active": false
}
],
"path": "http://192.168.56.101:8000/api/comments",
"per_page": 3,
"to": 12,
"total": 100
}
}
整形した34ページ目の結果
{
"comments": [
{
"id": 1,
"title": "最初のコメント",
"body": "最初のコメントです!",
"updated_at": "2023-10-02T10:10:10.000000Z"
}
],
"links": {
"first": "http://192.168.56.101:8000/api/comments?page=1",
"last": "http://192.168.56.101:8000/api/comments?page=34",
"prev": "http://192.168.56.101:8000/api/comments?page=33",
"next": null
},
"meta": {
"current_page": 34,
"from": 100,
"last_page": 34,
"links": [
{
"url": "http://192.168.56.101:8000/api/comments?page=33",
"label": "« Previous",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=1",
"label": "1",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=2",
"label": "2",
"active": false
},
{
"url": null,
"label": "...",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=25",
"label": "25",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=26",
"label": "26",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=27",
"label": "27",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=28",
"label": "28",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=29",
"label": "29",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=30",
"label": "30",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=31",
"label": "31",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=32",
"label": "32",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=33",
"label": "33",
"active": false
},
{
"url": "http://192.168.56.101:8000/api/comments?page=34",
"label": "34",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "http://192.168.56.101:8000/api/comments",
"per_page": 3,
"to": 100,
"total": 100
}
}