Laravelは簡単にRestful APIを実装できるようになっています。
バージョン
- PHP 7.1.27
- Laravel 5.8.8
もくじ
実装のポイント
コントローラを作る時に–resourceオプション
$ php artisan make:controller MonsterController --resource
ルートにresourceのメソッドを指定する
$ vi ./routes/api.php Route::resource('monsters', 'MonsterController');
これだけです。
RESTfulの特徴
メソッドで動作を変化させるシンプル設計
- 一覧取得 index()
GET /api/monsters/ - IDを指定して取得
GET /api/monsters/{id} - 新規追加
POST /api/monsters/ - 更新
PUT /api/monsters/ - IDを指定して削除
DELETE /api/monsters/{id}
URLはそろえてHTTPメソッドで動作を変化させる
ステートレス
セッションなどの状態を管理せず、リクエストで必ず処理が完了する
URLの設計
- https://api.example.net/
apiとすぐにわかるURLにする。 - 拡張子をつけてはいけない
- URLが変わることがあってはいけない
- URLは複数名刺にする、バージョンを含める、動詞はつけない
http://api.example.net/v1/monsters/
// 今回の記事は良くないURLになっています。
モデルの作成
$ mkdir ./app/Models $ php artisan make:model Models/Monster -m Model created successfully. Created Migration: 2019_03_31_075556_create_monsters_table
プライマリキーの設定
$ vi ./app/Models/Monster.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Monster extends Model { protected $primaryKey = 'monster_id'; }
テーブルの定義
$ vi ./database/migrations/2019_03_31_082838_create_monsters_table.php <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMonstersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('monsters', function (Blueprint $table) { $table->bigIncrements('monster_id'); $table->string('name'); $table->string('voice'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('monsters'); } }
シーダファイルの作成
$ php artisan make:seeder MonstersTableSeeder
$ vi ./database/seeds/MonstersTableSeeder.php <?php use Illuminate\Database\Seeder; class MonstersTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table("monsters")->insert([ "monster_id" => 1, "name" => "komaさん", "voice" => "もんげぇ", "created_at" => new DateTime(), "updated_at" => new DateTime() ]); DB::table("monsters")->insert([ "monster_id" => 1, "name" => "Nyanchuuさん", "voice" => "ミーだにゃぁ", "created_at" => new DateTime(), "updated_at" => new DateTime() ]); } }
レコードの削除+テーブルの作成
$ php artisan migrate:refresh --seed
シーダファイルによるインサート
$ php artisan db:seed --class=MonstersTableSeeder
ミドルウェアの作成
JSONの日本語文字化け防止
(外部サイト:https://nextat.co.jp/staff/archives/203)
$ php artisan make:middleware UnescapeJsonResponse
$ vi ./app/Http/Middleware/UnescapeJsonResponse.php <?php namespace App\Http\Middleware; use Closure; use Symfony\Component\HttpFoundation\JsonResponse; class UnescapeJsonResponse { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $response = $next($request); // JSON以外はそのまま if (!$response instanceof JsonResponse) { return $response; } // エンコードオプションを追加して設定し直す $newEncodingOptions = $response->getEncodingOptions() | JSON_UNESCAPED_UNICODE; $response->setEncodingOptions($newEncodingOptions); return $response; } }
Laravel Kernelに追加
$ vi ./app/Http/Kernel.php protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ※下記を追加 'UnescapeJsonResponse' => \App\Http\Middleware\UnescapeJsonResponse::class, ];
コントローラの作成
$ php artisan make:controller MonsterController --resource
初期状態確認
$ cat ./app/Http/Controllers/MonsterController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class MonsterController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { // } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // } }
コントローラ内容変更
$ vi ./app/Http/Controllers/MonsterController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Monster; class MonsterController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function __construct() { $this->middleware('UnescapeJsonResponse'); } public function index() { $items = Monster::all(); return response()->json($items); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required|max:255', 'voice' => 'required|max:255', ]); $monster = new Monster(); $monster->name = $request->name; $monster->voice = $request->voice; $monster->save(); return response()->json(); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { $item = Monster::find($id); return response()->json($item); } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { $this->validate($request, [ 'name' => 'required|max:255', 'voice' => 'required|max:255', ]); $monster = Monster::find($id); $monster->name = $request->name; $monster->voice = $request->voice; $monster->save(); return response()->json(); } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy(Request $request, $id) { $monster = Monster::find($id); $monster->delete(); return response()->json(); } }
ルート設定
$ vi ./routes/api.php <?php use Illuminate\Http\Request; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); ※下記を追加 Route::resource('monsters', 'MonsterController');
ルーティング確認
$ php artisan route:list +--------+-----------+-----------------------------+------------------+------------------------------------------------+--------------------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+-----------------------------+------------------+------------------------------------------------+--------------------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/monsters | monsters.index | App\Http\Controllers\MonsterController@index | api,UnescapeJsonResponse | | | POST | api/monsters | monsters.store | App\Http\Controllers\MonsterController@store | api,UnescapeJsonResponse | | | GET|HEAD | api/monsters/create | monsters.create | App\Http\Controllers\MonsterController@create | api,UnescapeJsonResponse | | | GET|HEAD | api/monsters/{monster} | monsters.show | App\Http\Controllers\MonsterController@show | api,UnescapeJsonResponse | | | PUT|PATCH | api/monsters/{monster} | monsters.update | App\Http\Controllers\MonsterController@update | api,UnescapeJsonResponse | | | DELETE | api/monsters/{monster} | monsters.destroy | App\Http\Controllers\MonsterController@destroy | api,UnescapeJsonResponse | | | GET|HEAD | api/monsters/{monster}/edit | monsters.edit | App\Http\Controllers\MonsterController@edit | api,UnescapeJsonResponse | | | GET|HEAD | api/user | | Closure | api,auth:api | +--------+-----------+-----------------------------+------------------+------------------------------------------------+--------------------------+
index()確認
http://IPアドレス/api/monsters
show()確認
http://IPアドレス/api/monsters/2