WEBその他

【Laravel】ResourceCollectionを利用したAPI Responseの整形

WEB
この記事は約9分で読めます。

implの藤谷です。
LaravelのresourceCollectionでresponseを整形する方法を解説します。

ResourceCollection

データをAPIレスポンスとして適切な形式に整形することを目的とするclassです。
カスタム属性の追加やオーバーフェッチの防止、異なるエンドポイントで同じ形式のレスポンスを返却する際の共通化等に使用します。

実装

Userモデル(ユーザー)とArticlesモデル(投稿)を用意し、「ユーザーの一覧と、ユーザー毎の投稿内容」の取得をケースとして実装します。
3人のユーザーが存在し、「testUser3」のユーザーが二件の投稿をしている想定です。

resourceクラス作成

まず、userとarticleのresourceクラスを作成します。
下記のコマンドを実行すると、App\Http|Resource/ 配下に二つのresourceクラスが生成されます。

$ php artisan make:resource UserResource

$ php artisan make:resource ArticleResource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ArticleResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }
}
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }
}

ArticleResource実装

articleモデルからは`id, title, content`の3つを取得します。

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ArticleResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
        ];
    }
}

作成したresourceクラスをuseで読み込む事で他のクラスで使用できるようになります。
メソッドの返却値にJaonResponseの型付けを行なって値を確認してみます。

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Article;
use App\Http\Resources\ArticleResource;
use Illuminate\Http\JsonResponse;

class ArticlesController extends Controller
{
public function index(): JsonResponse
    {
        $articles = Article::all();

        return response()->json([
            'status_code' => 201,
            'articles' => ArticleResource::collection($articles)
        ]);
    }
}
{
    "status_code": 201,
    "articles": [
         {
            "id": 1,
            "title": "title01",
            "content": "content01"
        }
        {
            "id": 2,
            "title": "title02",
            "content": "content02"
        },
    ]
}

UserResource実装

次に、UserResourceを実装し、Articleとのリレーションを組んだ状態で値を返す実装を行います。

まず、UserとArticleのリレーションを一対多で組みます。

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

// class User extends Model
class User extends Authenticatable
{
    public function articles(): HasMany
    {
        return $this->hasMany(Article::class);
    }
}
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Models\User;

class Article extends Model
{
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

モデルの設定後、UserResourceにデータ取得のコードを記述します。
ArticleResourceを読み込み、ユーザーに紐付く投稿を取得しています。

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'article' => ArticleResource::collection($this->whenLoaded('articles'))
        ];
    }
}

resource作成後、controllerで呼び出して要件通りのレスポンスを取得できることを確認します。
testUser3のユーザーが二件投稿をしていたので、それを正しく取得できています。

{
    "status_code": 200,
    "users": [
        {
            "id": 2,
            "name": "testUser1",
            "email": "testUser1@i.com",
            "article": []
        },
        {
            "id": 2,
            "name": "testUser2",
            "email": "testUser2@i.com",
            "article": []
        },
        {
            "id": 3,
            "name": "testUser3",
            "email": "testUser3@i.com",
            "article": [
                {
                    "id": 1,
                    "title": "title1",
                    "content": "content1"
                },
                {
                    "id": 2,
                    "title": "title2",
                    "content": "content2"
                }
            ]
        }
    ]
}

終わりに

今回は以上になります。
ここまで読んでいただき、ありがとうございました。