Laravel・PHP入門

PHPer初心者

【Laravel】APIリソースを使う(Json/Resource)

LaravelのAPIリソースを使ってとても便利だと思ったのでまとめ。

https://readouble.com/laravel/5.5/ja/eloquent-resources.html

APIリソースとは(日本訳ドキュメント引用)

API構築時、Eloquentモデルと、アプリケーションユーザーに対して実際に返信するJSONリスポンスとの間に、
トランスレーション層を設置することが必要となります。
Laravelのリソースクラスは、モデルやモデルコレクションを記述しやすく簡単に、JSONへと変換してくれます。

ちなみにLaravel5.5で登場したそうです。最高です。

実装するもの

  • /posts でPost一覧を取得するAPIで返却するリソース
  • /posts/{id}でIDが1のPostを取得するAPIで返却するリソース

完成形

  • 一覧

f:id:fresh_engineer:20180706235357p:plain

  • ID指定

f:id:fresh_engineer:20180706235431p:plain

こんな感じです。

前準備

  • 環境構築(Laravelプロジェクト)
  • Controllerの作成
  • Modelの作成
  • Route通す
  • DBにPostとCommentを準備

用意したデータ

  • postsテーブル

f:id:fresh_engineer:20180707001326p:plain

  • commentsテーブル

f:id:fresh_engineer:20180707001353p:plain

ディレクトリ構成

App
┗Http
  ┝Controllers
    ┝BaseController.php
    ┝PostController.php
    ┗CommentController.php
  ┝Resources
    ┝BaseResource.php
    ┝Post.php
    ┗Comment.php
  ┝Models
    ┝BaseModel.php
    ┝Post.php
    ┗Comment.php
  ┝Repositories
    ┝BasePost.php
    ┝Post.php
    ┗Comment.php
  ┗Services
    ┝BaseService.php
    ┝Post.php
    ┗Comment.php

深くなりましたが
APIリソースに深く関わるのは
・取得するデータのロジックを記載するRepository
インスタンス化するService
・レスポンスを定義するResourceクラス
この3つです。

イメージを明確にするために、補足でController/ Route/ Modelは画像のみ貼っておきます。

Route

f:id:fresh_engineer:20180707000006p:plain

Controller

f:id:fresh_engineer:20180707000018p:plain

Model

※PostにひもづくCommentも取得したいのでリレーション定義
f:id:fresh_engineer:20180707000457p:plain

リソースに渡すモデルを取得

今回はRepositoryクラスに実装します。
f:id:fresh_engineer:20180707000057p:plain

リソースの作成

php artisan make:resource PostResource

まずはリソースクラスを作成します。

  • Postリソース

今回はPost一覧を求められた場合、
Postのもつid,title,body,とそのPostにひもづくCommentを取得したい。
渡したい情報をそれぞれキー(クラスにより自由です)、
そして渡したモデルの誰がそのデータを持っているか渡してあげます。

CommentはPostと1対多の関係、つまりは1Postが複数のCommentを持ちうるので
Commentリソースについてcollectionメソッドを用います。

このように、APIリソースはそれらのリレーションで紐付く情報も
プロパティから呼び出して別のリソースを整形して同時に返却することができます。

<?php
namespace App\Http\Resources;

use App\Http\Resources\Comment as CommentResource;
use Illuminate\Http\Resources\Json\Resource;

class Post extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'comments' => CommentResource::collection($this->comments)
        ];
    }
}
  • Commentリソース

コメントではid,post_id,bodyを返却したいのでそれぞれ定義して返します。

<?php
namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class Comment extends Resource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'post_id' => $this->post_id,
            'body' => $this->body
        ];
    }
}

リソースをインスタンス化する

リソースを実際に使用するときは、DBから取得したデータを
引数として渡しつつ、インスタンス化します。

リソースが1つの時は newでインスタンス化すれば良いですが、
複数のコレクションの場合はcollectionメソッドを用います。

<?php
namespace App\Services;

use App\Http\Resources\Post as PostResource;
use App\Repositories\Post as PostRepository;

class Post extends BaseService
{
    private $post_repository;

    public function __construct(PostRepository $post_repository)
    {
        $this->post_repository = $post_repository;
    }

    /**
     * 全てのPostを取得し成型してJsonで返す
     *
     * @return mixed
     */
    public function getAllPosts()
    {
         return PostResource::collection($this->post_repository->getAllPosts());
    }

    /**
     * IDからPostを取得し成型してJsonで返す
     *
     * @param int $id
     * @return mixed
     */
    public function getPostById(int $id)
    {
        return new PostResource($this->post_repository->getPostById($id));
    }
}


これで、URLを叩くと
f:id:fresh_engineer:20180706235431p:plain
f:id:fresh_engineer:20180706235357p:plain
データが返ってきましたね!

リソースはいくつも階層重ねることもできるので、
ドメイン的な整形の仕方にももってこいですね。
積極的に使っていきたいです

以上