Edited at

Laravel 5.4 と Vue.js 2.2 と JWTAuth で、ログインできる SPA アプリケーションのチュートリアル 2/4

More than 1 year has passed since last update.


目次

四部作です。


  1. はじめに

  2. Todoアプリ作成編 / サーバーサイド ← 今ここ

  3. Todoアプリ作成編 / フロントエンド

  4. 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

中身を編集します。


database/migrations/2017_03_16_140557_create_tasks_table.php

<?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.

編集します。


app/Task.php

<?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.

編集します。


app/Http/Controllers/TaskController.php

<?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で最初から別れているから便利だ。


routes/api.php

<?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好き。


database/factories/ModelFactory.php

<?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),
];
});



database/seeds/DatabaseSeeder.php

<?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を使うようにします。


phpunit.xml

        <!-- 追加 -->

<env name="DB_DATABASE" value=":memory:"/>

DB名を環境変数から読むようにします。


config/database.php

<?php

// ...

'connections' => [

'sqlite' => [

// ...
'database' => env('DB_DATABASE', database_path('database.sqlite')),
// ...


最後に、Laravelがテスト環境のDBを見てくれるように修正します。

デフォルトではキャッシュを見てしまうので・・・これ何とかならないのかな


tests/CreatesApplication.php

<?php

// ...

public function createApplication()
{
if (file_exists(__DIR__.'/../bootstrap/cache/config.php'))
unlink(__DIR__.'/../bootstrap/cache/config.php');
// ...


これで準備ができました。テストを書きます。


tests/Feature/TaskTest.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 とかあるので、

ログインがあるサイトのテストが簡単に書けます。

サーバーサイド編は以上です。