この記事は 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を使い分けると良さそうですね。