Laravel + Vue.js で出席管理Webアプリを作成する – Part.2

Laravel + Vue.js で出席管理Webアプリを作成する – Part.1」の続きになります。Part.1 ではLaravel開発環境のセットアップからダミーデータの作成まで行いました。今回から出席管理のAPIを作成していきます。

APIのルーティング設定

APIの仕様は以下のとおりです。

api/attendances (GET)全ての出席登録を取得して返す
api/attendances/:id (GET)指定されたIDの出席登録のみを取得して返す
api/attendances (POST)新しい出席を登録する
api/attendances/:id/report (PUT)通報用のエンドポイント

routes/api.phpを編集し、APIのルーティングを設定します。

routes/api.php

<?php
 
use Illuminate\Http\Request;
 
Route::resource('attendances', 'Api\AttendancesController')
    ->only(['index', 'show', 'store']);
 
Route::put('attendances/{attendance}/report', 'Api\ReportAttendancesController@update');

 Laravelのルーティング書き方まとめ – Qiita

Controllerの作成

API用のコントローラを用意していきます。

AttendancesController

$ php artisan make:controller Api/AttendancesController

app/Http/Controllers/Api/AttendancesController.phpが作成されるので、以下のように編集します。

app/Http/Controllers/Api/AttendancesController.php

<?php
 
namespace App\Http\Controllers\Api;
 
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Attendance;  // *1
use App\Http\Resources\AttendanceResource;  // *2
 
class AttendancesController extends Controller
{
    public function index()
    {
        $attendances = Attendance::latest()
            ->ignoreFlagged()  // *3
            ->paginate(20);
 
        return AttendanceResource::collection($attendances);
    }
 
    public function show(Attendance $attendance)
    {
        return new AttendanceResource($attendance);
    }
 
    public function store(Request $request)
    {
        $attendance = $this->validate( $request, [
            'name'  => 'required|min:3|max:50',
            'email' => 'required|email',
            'body'  => 'required|min:3',
        ] );
 
        $attendance = Attendance::create($attendance);
 
        return new AttendanceResource($attendance);
    }
}

未定義のメソッドがあるためブラウザ表示するとエラーになりますが、現段階では気にしなくてOKです。それぞれの意味は以下のようになります。

*1例えば、CakePHPではController::loadModel()でModelを呼び出しますが、Laravelでは単純に namespace の参照で使用します。
*2Resourceクラス(後述)です。公式ドキュメント
*3クエリースコープです。 参考記事

クエリースコープの設定

ignoreFlaggedスコープを設定するため、Model側を編集します。

app/Attendance.php

<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Attendance extends Model
{
    protected $fillable = [
        'name',
        'email',
        'body',
        'flagged_at',
        'updated_at',
    ];
 
    // 以下を追加
 
    /**
     * [QueryScope] 通報済みユーザの除外
     * 
     * @param $query
     * @return mixed
     */
    public function scopeIgnoreFlagged($query)
    {
        return $query->where('flagged_at', null);
    }
}

ignoreFlagged()というクエリースコープを用意することで、SQLのwhere句として`flagged_at` IS NULLという条件の役割を持たせることが可能になります。

先述した  クエリースコープの参考記事 にも記載されていますが、条件ごとにスコープを切ることができるため、可読性が良くなるメリットがあります。

ReportAttendancesController

$ php artisan make:controller Api/ReportAttendancesController

app/Http/Controllers/Api/ReportAttendancesController.phpが作成されるので、以下のように編集します。

app/Http/Controllers/Api/ReportAttendancesController.php

<?php
 
namespace App\Http\Controllers\Api;
 
use App\Http\Controllers\Controller;
use App\Http\Resources\AttendanceResource;
use App\Attendance;
 
class ReportAttendancesController extends Controller
{
    /**
     * 通報処理
     *
     * @param Attendance $attendance
     * @return AttendanceResource
     */
    public function update( Attendance $attendance )
    {
        $attendance->flag();
 
        return new AttendanceResource($attendance);
    }
}

通報処理を行うflag()メソッドをModel側に作成します。

app/Attendance.php

<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;  // 追加
 
class Attendance extends Model
{
    // 中略
 
    /**
     * 通報処理(flagged_at の更新)
     * 
     * @return bool
     */
    public function flag()
    {
        return $this->update( [ 'flagged_at' => now() ] );
    }
}

Resourceクラスの作成

Resourceクラスは、返却するデータの構造やフォーマットを調整する役割を担います。

AttendanceResource

$ php artisan make:resource AttendanceResource

app/Http/Resources/AttendanceResource.phpが作成されるので、以下のように編集します。

app/Http/Controllers/Api/AttendancesController.php

<?php
 
namespace App\Http\Resources;
 
use Illuminate\Http\Resources\Json\Resource;
 
class AttendanceResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id'     => $this->id,
            'name'   => $this->name,
            'avatar' => $this->avatar,  // *1
            'body'   => $this->body,
            'date'   => $this->created_at->diffForHumans()  // *2
        ];
    }
}
*1スキーマには存在しないデータですが、アバター画像を取得するメソッドを用意します(後述)。
*2 CarbondiffForHumans()メソッドを使用しています。Laravelではデフォルトでcreated_atupdated_atフィールドに対して使用可能です。公式ドキュメント

次に、 Gravatarからアバター画像を取得するメソッドをModel側に追加しておきます。

app/Attendance.php

public function getAvatarAttribute()
{
    return sprintf( 'https://www.gravatar.com/avatar/%s?s=100', md5( strtolower( trim( $this->email ) ) ) );
}

GravatarのAPIでは、メールアドレスをmd5ハッシュしたパラメータを渡すとアバター画像のURLを取得することができます。
 PHP Image Requests – Gravatar

上記のように、get[プロパティ名]Attribute()という命名規則でメソッド定義しておくことで、スキーマには存在しないような独自の値を返却することも可能になります。

動作確認

APIの動作確認には Postmanが便利です。

一覧表示 – api/attendances (GET)

http://homestead.test/api/attendancesにGETでリクエストします。 正常にレスポンスが返ってきたらOKです(以下はPostmanの画面キャプチャ)。

個別表示 – api/attendances/:id (GET)

出席登録 – api/attendances (POST)

ユーザの通報 – api/attendances/:id/report (PUT)

以上で Part.2 は終了です。
Part.3 からVue.jsを使用したビューを作成していきます。