目次
四部作です。
- Todoアプリ作成編 / サーバーサイド ← 今ここ
- Todoアプリ作成編 / フロントエンド
- JWTAuthでログイン編
今回は、LaravelのREST APIの機能を使った、APIサーバーの実装がメインです。
TODOアプリなので、 tasks
テーブルを作り、そこへの RESTFul
なルーティングを定義します。
ユニットテストも作ります。
Migration
まずは tasks
テーブルを作成するマイグレーションを行います。
php artisan make:migration create_tasks_table
# => Created Migration: 2017_03_16_140557_create_tasks_table
中身を編集します。
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable(false);
$table->boolean('is_done')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
実行します。
php artisan migrate
# => Migrated: 2017_03_16_140557_create_tasks_table
Model
次にモデルを作ります。
php artisan make:model Task
# => Model created successfully.
編集します。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = ['name', 'is_done'];
protected $casts = [
'is_done' => 'boolean',
];
}
$casts
は、APIサーバーとしてJSONを送る際に 1
=> true
に変換されるようにする設定です。
Controller
tasks
テーブルに関連するRESTコントローラーを作ります。
php artisan make:controller TaskController
# => Controller created successfully.
編集します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Task;
class TaskController extends Controller
{
public function index()
{
return Task::take(5)->get()->keyBy('id');
}
public function store(Request $request)
{
return Task::create($request->only('name'))->save()->fresh();
}
public function destroy($id)
{
return Task::destroy($id);
}
public function update($id, Request $request)
{
return Task::find($id)->fill($request->only('is_done'))
->save()->fresh();
}
}
エラー処理、バリデーション、ポリシーの設定してない、
ステータスコード指定してない、
とか色々ツッコミどころはありますが、今回はこのまま突き進みます。
Routing
APIのルーティングを設定します。
Laravelは5.4からWebとAPIで最初から別れているから便利だ。
<?php
use Illuminate\Http\Request;
Route::group(['middleware' => 'api'], function () {
Route::resource('tasks', 'TaskController');
});
これで、RESTなコントローラーのルーティングが設定されます。
c.f) https://laravel.com/docs/5.4/controllers#resource-controllers
では実験してみます。
curl -XGET localhost:8000/api/tasks
[]
curl -XPOST localhost:8000/api/tasks -d 'name=Learn Vue.js'
{
"1": {
"id": 1,
"name": "Learn Vue.js",
"is_done": false,
"created_at": "2017-03-16 22:09:13",
"updated_at": "2017-03-16 22:09:13"
}
}
curl -XPUT localhost:8000/api/tasks/1 -d 'is_done=true'
{
"id": 1,
"name": "Learn Vue.js",
"is_done": true,
"created_at": "2017-03-16 22:09:13",
"updated_at": "2017-03-16 22:12:56"
}
curl -XDELETE localhost:8000/api/tasks/1
curl -XGET localhost:8000/api/tasks
[]
OKです。
Seeding
テストデータを毎回作るのは意外と手間なので、自動化します。
こういうのがやりやすいのでLaravel好き。
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\Task::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'is_done' => mt_rand(0, 1),
];
});
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(App\Task::class, 50)->create();
}
}
データを投入します。
php artisan db:seed
# 確認
curl -XGET localhost:8000/api/tasks
{
"1": {
"id": 1,
"name": "Thora Strosin",
"is_done": false,
"created_at": "2017-03-16 22:39:49",
"updated_at": "2017-03-16 22:39:49"
},
"2": {
# ...
},
"5": {
"id": 5,
"name": "August Denesik",
"is_done": true,
"created_at": "2017-03-16 22:39:49",
"updated_at": "2017-03-16 22:39:49"
}
}
データベースを初期化したい時は、
php artisan migrate:refresh --seed
で綺麗な状態のDBが手に入ります。お薦め。
Unit Test
勢いでテストも書いちゃいましょう。
まず、テスト環境ではインメモリSQLiteを使うようにします。
<!-- 追加 -->
<env name="DB_DATABASE" value=":memory:"/>
DB名を環境変数から読むようにします。
<?php
// ...
'connections' => [
'sqlite' => [
// ...
'database' => env('DB_DATABASE', database_path('database.sqlite')),
// ...
最後に、Laravelがテスト環境のDBを見てくれるように修正します。
デフォルトではキャッシュを見てしまうので・・・これ何とかならないのかな
<?php
// ...
public function createApplication()
{
if (file_exists(__DIR__.'/../bootstrap/cache/config.php'))
unlink(__DIR__.'/../bootstrap/cache/config.php');
// ...
これで準備ができました。テストを書きます。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class TaskTest extends TestCase
{
use DatabaseMigrations;
use DatabaseTransactions;
public function testCrudTask()
{
$this->json('POST', '/api/tasks', ['name' => 'Learn Vue.js'])
->assertStatus(200)
->assertJson([
'id' => 1,
'name' => 'Learn Vue.js',
'is_done' => false,
]);
$this->assertDatabaseHas('tasks', [
'id' => 1,
'name' => 'Learn Vue.js',
'is_done' => false,
]);
$this->json('GET', '/api/tasks')
->assertStatus(200)
->assertJson([
1 => [
'id' => 1,
'name' => 'Learn Vue.js',
'is_done' => false,
]
]);
$this->json('PUT', '/api/tasks/1', ['is_done' => true])
->assertStatus(200)
->assertJson([
'id' => 1,
'name' => 'Learn Vue.js',
'is_done' => true,
]);
$this->assertDatabaseHas('tasks', [
'id' => 1,
'name' => 'Learn Vue.js',
'is_done' => true,
]);
$this->json('DELETE', '/api/tasks/1')
->assertStatus(200);
$this->assertDatabaseMissing('tasks', [
'id' => 1,
]);
}
}
これでいいのかな・・・冗長な気もするが
参考
http://qiita.com/komatzz/items/1679373c86c252c5f49f
実行してみます。
./vendor/bin/phpunit
PHPUnit 5.7.16 by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
Time: 107 ms, Memory: 14.00MB
OK (2 tests, 11 assertions)
通りました。
APIサーバーのテストは単純で楽そう。大規模になったら大変なのかな・・・
特にLaravelは actingAs
とか WithoutMiddlware
とかあるので、
ログインがあるサイトのテストが簡単に書けます。
サーバーサイド編は以上です。