WEBOPIXEL

LaravelでCRUD APIを作成する

Posted: 2018.10.30 / Category: PHP / Tag: 

LaravelでSPA作るメモ第3回目です。今回はCRUD APIを作成してテストまでやってみます。

Sponsored Link

使用環境
Laravel 5.7
jwt-auth 1.0.0-rc3

この記事は下記の続きです。

Laravel + JWTAuth + Vue.js でAPIログイン認証の実装
Laravel + Vue.js にVuexを導入する

データベースの作成

データベースを作成するためマイグレーションファイルを作成します。
artisanで雛形を作成して、

$ php artisan make:migration create_tasks_table

スキーマは下記のようにします。

database/migrations/0000_00_00_000000_create_tasks_table.php

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTasksTable extends Migration
{
	public function up()
	{
		Schema::create('tasks', function (Blueprint $table) {
			$table->increments('id');
			$table->string('title');
			$table->integer('state_id')->unsigned()->default(1);
			$table->integer('user_id')->references('id')->on('users')->unsigned()->index()->nullable();
			$table->timestamp('due_at')->nullable()->comment('期日');
			$table->timestamps();
		});
	}

	public function down()
	{
		Schema::dropIfExists('tasks');
	}
}

シーダーもartisanで雛形を作成して、

$ php artisan make:seeder TasksTableSeeder

ダミーデータを適当に作成します。

database/seeds/TasksTableSeeder.php

<?php
use Illuminate\Database\Seeder;

class TasksTableSeeder extends Seeder
{
	public function run()
	{
		DB::table('tasks')->insert([
			[
				'title' => 'はじめてのタスク',
				'state_id' => 1,
				'user_id' => 1,
				'due_at' => '2018-10-02 14:28:19',
				'created_at' => '2018-10-02 14:28:19',
				'updated_at' => '2018-10-02 14:28:19'
			],[
				'title' => '2番目のタスク',
				'state_id' => 2,
				'user_id' => 1,
				'due_at' => '',
				'created_at' => '2018-10-04 14:28:19',
				'updated_at' => '2018-10-04 14:28:19'
			]
		]);
	}
}

DatabaseSeeder.phpに作成したシーダーを追記します。

database/seeds/DatabaseSeeder.php

public function run()
{
	$this->call(UsersTableSeeder::class);
	$this->call(TasksTableSeeder::class);
}

artisanでデータベーステーブルを作成しましょう。

$ php artisan migrate
$ php artisan db:seed --class=TaskTableSeeder

モデルの作成

Taskモデルを作成します。

$ php artisan make:model Models\Task

app/Models/Task.php

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
	/**
	* @var array
	*/
	protected $fillable = [
		'title', 'state_id', 'user_id', 'due_at'
	];
}

リポジトリの作成

モデルとコントローラーの間にリポジトリを挟んでみます。
あとで変えるかもですが、とりあえずこんな感じ。

app/Repositories/Repository.php

<?php
namespace App\Repositories;

use Illuminate\Database\Eloquent\Model;

class Repository
{
	/**
	* @var Model
	*/
	protected $model;

	/**
	* @param Model $model
	*/
	public function __construct(Model $model)
	{
		$this->model = $model;
	}

	/**
	* @return \Illuminate\Database\Eloquent\Collection
	*/
	public function all()
	{
		return $this->model->all();
	}

	/**
	* @param array $data
	* @return mixed
	*/
	public function store(array $data)
	{
		return $this->model->create($data);
	}

	/**
	* @param array $data
	* @param int $id
	* @return mixed
	*/
	public function update(array $data, int $id)
	{
		$record = $this->model->find($id);
		return $record->update($data);
	}

	/**
	* @param int $id
	* @return int
	*/
	public function delete(int $id)
	{
		return $this->model->destroy($id);
	}

	/**
	* @param int $id
	* @return mixed
	*/
	public function findById(int $id)
	{
		return $this->model->findOrFail($id);
	}
}

リクエストの作成

バリデーションの設定とかをここで書きます。
とりあえずタイトルを必須に。

app/Repositories/Repository.php

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TaskRequest extends FormRequest
{
	/**
	* @return bool
	*/
	public function authorize()
	{
		return true;
	}

	/**
	* @return array
	*/
	public function rules()
	{
		return [
			'title' => 'required'
		];
	}

	/**
	* @return array
	*/
	public function messages()
	{
		return [
			'title.required' => 'タイトルは必ず入力してください。',
		];
	}
}

コントローラーの作成

コントローラーは基本response()->json()で返します。
LaravelでViewを持つ必要がないので、CreateとEditはいらないですね。

app/Http/Controllers/TasksController.php

<?php
namespace App\Http\Controllers;

use App\Models\Task;
use App\Repositories\Repository;
use App\Http\Requests\TaskRequest;

class TasksController extends Controller
{
	/** @var Repository */
	protected $repository;

	/**
	* TasksController constructor.
	* @param Task $task
	*/
	public function __construct(Task $task)
	{
		$this->repository = new Repository($task);
	}

	/**
	* @return \Illuminate\Http\JsonResponse
	*/
	public function index()
	{
		$tasks = $this->repository->all();
		return response()->json($tasks);
	}

	/**
	* @param $id
	* @return \Illuminate\Http\JsonResponse
	*/
	public function show($id)
	{
		return response()->json($this->repository->findById($id));
	}

	/**
	* @param TaskRequest $request
	* @return \Illuminate\Http\JsonResponse
	*/
	public function store(TaskRequest $request)
	{
		$response = $this->repository->store($request->all());
		return response()->json($response, 201);
	}

	/**
	* @return \Illuminate\Http\JsonResponse
	*/
	public function update(TaskRequest $request, int $id)
	{
		$response = $this->repository->update($request->all(), $id);
		return response()->json($response, 200);
	}

	/**
	* @param $id
	* @return int
	*/
	public function destroy($id)
	{
		return $this->repository->delete($id);
	}
}

ルーターの追記

作成したコントーラーにアクセスする為にルーターに追記します。
ログインしないとアクセスできないようにする為、auth:apiの中に書きます。

routes/api.php

<?php
Route::group(['middleware' => 'auth:api'], function () {
	Route::get('/me', 'AuthController@me');
	Route::post('/logout', 'AuthController@logout');

	// Task
	Route::resource('tasks', 'TasksController',
		['only' => ['index', 'show', 'store', 'update', 'destroy']]
	);
});

テストファイルの作成

最後に作成したAPIが問題なく動くか簡単にテストしてみます。

Laravelのテスト環境の構築は下記を参考にしてください。

LaravelのUnitTestでテスト時はデータベースを切り替える

このAPIはすべてログインしないと使用できないので、setUpactingAsでログイン状態になるように指定します。
あとはシーダーも実行しておきましょう。

routes/api.php

<?php
namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\User;

class TaskTest extends TestCase
{
	use RefreshDatabase;

	public function setUp()
	{
		parent::setUp();
		$this->artisan('db:seed');

		// ログイン認証
		$user = factory(User::class)->create();
		$this->actingAs($user, 'api');
	}

	/**
	* @test
	*/
	public function GETでアクセスするとjsonが返却()
	{
		$response = $this->get('api/tasks');
		$this->assertThat($response->content(), $this->isJson());
	}

	/**
	* @test
	*/
	public function SHOWで取得情報は正しいか()
	{
		$response = $this->get('api/tasks/1');
		$response->assertJson([
			'title' => 'はじめてのタスク'
		])->assertStatus(200);
	}

	/**
	* @test
	*/
	public function GETで取得できるjsonは要件通りである()
	{
		$response = $this->get('api/tasks');
		$tasks = $response->json();
		$task = $tasks[0];
		$this->assertSame(['id', 'title', 'state_id', 'user_id', 'due_at', 'created_at', 'updated_at'], array_keys($task));
	}

	/**
	* @test
	*/
	public function GETの取得件数は2件である()
	{
		$response = $this->get('api/tasks');
		$response->assertJsonCount(2);
	}

	/**
	* @test
	*/
	public function POSTでデータが追加される()
	{
		$params = [
			'title' => 'テストタイトル',
			'state_id' => '1',
			'user_id' => '1'
		];
		$this->postJson('api/tasks', $params);
		$this->assertDatabaseHas('tasks', $params);
	}

	/**
	* @test
	*/
	public function POSTでtitleが未入力のエラー()
	{
		$params = [
			'state_id' => '2',
			'user_id' => '2'
		];
		$response = $this->postJson('api/tasks', $params);
		$response->assertJsonFragment(['title' => ['タイトルは必ず入力してください。']]);
	}

	/**
	* @test
	*/
	public function UPDATEで情報は更新されるか()
	{
		$params = [
			'title' => '書き換えたタイトル'
		];
		$respose = $this->putJson('api/tasks/1', $params);

		$this->assertTrue($respose->json());
		$this->assertDatabaseHas('tasks', $params);
	}

	/**
	* @test
	*/
	public function DALETEで情報は削除されるか()
	{
		$params = [
			'id' => 1
		];
		$this->deleteJson('api/tasks/1');
		$this->assertDatabaseMissing('tasks', $params);
	}
}

以上でAPIの実装は終わりです。
次回はVue.jsで実際に操作できるようにしていきたいと思います。

ここまでのソースコードはGithubに載せてあります。
LaravelTodoSPA

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応