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

# 目次

四部作です。

1. [はじめに](http://qiita.com/acro5piano/items/908cd751b2ea97e19be1)
- Todoアプリ作成編 / サーバーサイド       **← 今ここ**
- [Todoアプリ作成編 / フロントエンド](http://qiita.com/acro5piano/items/a2c30bdd2b6f1a0d9c7d)
- [JWTAuthでログイン編](http://qiita.com/acro5piano/items/eb29f13b82f386220460)

今回は、LaravelのREST APIの機能を使った、APIサーバーの実装がメインです。

TODOアプリなので、 `tasks` テーブルを作り、そこへの `RESTFul` なルーティングを定義します。

ユニットテストも作ります。

# Migration

まずは `tasks` テーブルを作成するマイグレーションを行います。

```bash
php artisan make:migration create_tasks_table

# => Created Migration: 2017_03_16_140557_create_tasks_table
```

中身を編集します。

```php: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');
    }
}
```

実行します。

```bash
php artisan migrate

# => Migrated: 2017_03_16_140557_create_tasks_table
```

# Model

次にモデルを作ります。

```bash
php artisan make:model Task

# => Model created successfully.
```

編集します。

```php: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コントローラーを作ります。


```bash
php artisan make:controller TaskController

# => Controller created successfully.
```

編集します。

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

```php: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

では実験してみます。


```bash
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: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),
    ];
});
```

```php: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();
    }
}
```

データを投入します。

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


```xml:phpunit.xml
        <!-- 追加 -->
        <env name="DB_DATABASE" value=":memory:"/>
```

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

```php:config/database.php
<?php
    // ...

    'connections' => [

        'sqlite' => [

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

最後に、Laravelがテスト環境のDBを見てくれるように修正します。
デフォルトではキャッシュを見てしまうので・・・これ何とかならないのかな

```php:tests/CreatesApplication.php
<?php

    // ...

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

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

```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

実行してみます。

```bash
./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` とかあるので、
ログインがあるサイトのテストが簡単に書けます。

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