27
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-03-16

目次

四部作です。

  1. はじめに

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

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

27
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?