この記事は Web × PHP TechCafe Advent Calendar 2019 の12/15の記事です。
LaravelでモデルをREST APIのレスポンスとして返す際には、コントローラからモデルのCollectionやインスタンスを返すことで、レスポンスはJSON形式で返されます。しかし、モデルのプロパティを変形してからJSONのレスポンスとして返したいこともあります。
Laravelの API Resource を利用することで、REST APIのレスポンスとして返すJSONの値を柔軟に設定できます。時刻のフォーマットを変換してJSONに埋め込むような場合に便利です。
テーブル構造の例
例として、決まった日付の範囲で、日付ごとのスケジュールを編集するシステムを考えます。日付の範囲を格納するテーブルを dates
, スケジュールのデータを格納するテーブルを schedules
とします。テーブル構造は以下に示します。
テーブル定義をマイグレーションファイルに記述します。dates
のテーブル構造を CreateDatesTable
クラス、 schedules
のテーブル構造を CreateSchedulesTable
クラスとしてそれぞれ定義します。(use
文やコメントは省略しています)
class CreateDatesTable extends Migration { public function up() { Schema::create('dates', function (Blueprint $table) { $table->bigIncrements('id'); $table->date('date'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('dates'); } }
class CreateSchedulesTable extends Migration { public function up() { Schema::create('schedules', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->text('description')->nullable(); $table->unsignedBigInteger('date_id'); $table->time('start'); $table->time('end'); $table->text('remarks')->nullable(); $table->timestamps(); $table->softDeletes(); $table->foreign('date_id')->references('id')->on('dates'); }); } public function down() { Schema::dropIfExists('schedules'); } }
モデルとAPI Resourceの実装
モデルの定義
テーブルをPHPのオブジェクトとして扱うために、 dates
と schedules
テーブルに対応するモデルクラス Date
と Schedule
クラスを定義します。外部キー制約を schedules
テーブルの date_id
カラムに設定したので、モデルにもリレーションを設定します。
class Date extends Model { protected $fillable = ['date']; }
class Schedule extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'description',
'date_id',
'start',
'end',
'remarks'
];
public function date()
{
return $this->belongsTo(Date::class);
}
}
JSONの構造とAPI Resource
スケジュールを以下のようなJSON形式のレスポンスで返すことを考えます。
{ "data": [ { "id": 1, "title": "PHP Conference Japan 2019", "description": "国内最大級のPHPのカンファレンス", "date": "2019-12-01", "start": "10:00", "end": "18:00", "remarks": "東京都大田区南蒲田1丁目20-20 大田区産業プラザ PiO" } ] }
JSONに含まれる date
には Date
クラスの date
プロパティの値を、 start
と end
はそれぞれ H:i
形式の文字列を設定します。
JSONレスポンスをAPI Resourceとして返すために、 Resource
クラスを作成します。雛形を作成するコマンドは以下の通りです。
php artisan make:resource ScheduleResource
雛形を作成したら、 toArray()
の内容を以下に示すコード例のように修正します。
class ScheduleResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'title' => $this->title, 'description' => $this->description, 'date' => $this->date->date, 'start' => Carbon::parse($this->start)->format('H:i'), 'end' => Carbon::parse($this->end)->format('H:i'), 'remarks' => $this->remarks, ]; } }
変数 $this
には Schedule
クラスのインスタンスが格納されているので、リレーション先の Date
クラスのオブジェクトも date
プロパティから取得できます。日付の文字列を取得するためには $this->date->date
を参照します。予定の開始時刻と終了時刻を示すプロパティ start
と end
を H:i
形式の文字列に変換するために、Laravelに組み込まれているライブラリ Carbon
を利用します。
コントローラからAPI Resourceを用いてレスポンスを生成
API Responseをコントローラのメソッドから返すことで、JSON形式のレスポンスが生成できます。単純にAPI Resourceを返す場合は、ルーティングで直接 Resource
クラスのインスタンスを返却することもできます。
モデルのインスタンス単体を Resource
として返す場合は、 Resource
クラスのインスタンスを返却することで実現できます。複数のモデルのインスタンスを Collection
として返したい場合は、 collection()
を利用します。 Resource
クラスのインスタンスを new
する際の引数はモデルクラスのインスタンスを、 collection()
の引数にはモデルのインスタンスの Collection
を指定します。
APIコントローラの index()
から、スケジュールを開始時刻順にソートしてREST APIとしてJSON形式のレスポンスを返すコード例は以下の通りです。
public function index() { return ScheduleResource::collection( Schedule::orderBy('date_id') ->orderBy('start') ->get() ); }
まとめ
LaravelのAPI Resourceを利用すれば、REST APIのレスポンスとして返すJSONを柔軟に変形できます。デフォルトでは JSONの data
フィールドに Resource
クラスの toArray()
で返されるデータが格納されますが、サービスプロバイダで Resource::withoutWrapping()
で data
キーを削除できます。
モデルのフィールドを変形する程度なら、モデルのアクセサを利用するという手段もあります。レスポンスで返すJSONのフィールドをどのように変形したいかによって、モデルのアクセサとAPI Resourceを使い分けると良さそうですね。