Laravel+MySQLでTodoアプリのAPIを作った。

# やりたいこと
- フロントエンドとバックエンドを切り分けたTodoアプリ制作。
- 自分がバックエンド、友達がフロントエンド。
- ちゃんとデータベースを使う。
- データの通信はJSON形式。

# 使ったもの
試行錯誤を重ね、以下のものを使用することにしました。

- フロントエンド
  - HTML
  - JS
- バックエンド
  - Laravel
  - MySQL

# 開発環境
- フロントエンド
  - Window10のなんか
- バックエンド
  - macOS Mojave 10.14.3
  - PHP 7.1.23
  - Laravel 5.8.5
  - MySQL 8.0.15

# フロントエンド側
HTMLとJSのみで必要最小限の実装を行いました。

ソースは下に貼っておきます。
<a href="https://github.com/pk-Musan/todo-front"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/pk-Musan/todo-front.png" width="460px"></a>

## ディレクトリの構成
- todo-front
  - index.html
  - static / js / script.js

(jsの部分は階層構造をmarkdownで書ききれなかっただけです。staticの中のjsの中にscript.jsがあるという意味です。)


## 画面
<img width="842" alt="スクリーンショット 2019-03-21 17.34.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/14240958-7d00-08cf-bb31-8fed08a628e7.png">

## ソースコード
### ■ index.html
解説する必要なし

```index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ToDo</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
    <h2>ToDo</h2>
    <hr>
    <div class="content">
        <div class="post_form">
            Taskを追加:
            <input type="text" id="new_task">
            <input type="submit" value="送信" onclick="postText();">
        </div>
        <br>
        <div>Task一覧:
        </div>
        <div>
            <ul id="list">
            </ul>
        </div>
    </div>
</body>
<script src="./static/js/script.js"></script>
</html>
```

### ■ script.js

|関数名|説明|
|:--|:--|
|getText|全てのタスクをくれ。って言う関数|
|postText|一つのタスクをSQLに追加してね。終わったら全てのタスクをくれ。って言う関数|
|showTask|HTML内に全てのタスクを追加する関数|

```script.js
var url = "http://127.0.0.1:8000/api/todos";
var tasks = [];

function getText(){
    $.ajax({
        type: "GET",
        url: url,
        datatype: JSON,
        cache: false,
        contentType: false,
        processData: false
    }).done(function(response){
        tasks = [];
        for(var i=0; i<response.length; i++)
            tasks[i] = response[i].memo;
        showTask();
    }).fail(function(){
        alert('Task受信に失敗しました')
    });
}

function postText(){
    const new_task = $("#new_task").get(0).value;
    if(new_task == ""){
        alert("入力してください");
        return;
    }

    console.log(new_task);

    var data = {
        memo: new_task,
        status: 0
    };

    $.ajax({
        type: "POST",
        url: url,
        data: JSON.stringify(data),
        datatype: JSON,
        cache: false,
        contentType: false,
        scriptCharset: 'utf-8'
    }).done(function(response){
        tasks = [];
        for(var i=0; i<response.length; i++)
            tasks[i] = response[i].memo;
        clearText();
        showTask();
    }).fail(function(){
        alert('Task送信に失敗しました')
    });
}

function showTask(){
    $("#list").empty('');

    var fragment = document.createDocumentFragment();

    for(var i=0; i<tasks.length; i++){
        var $li = document.createElement('li');
        var task = document.createTextNode(tasks[i]);
        $li.appendChild(task);
        fragment.appendChild($li);
    }

    $("#list").get(0).appendChild(fragment);
}

function clearText(){
    $("#new_task").get(0).value = "";
}

function init(){
    getText();
}

init();
```




# バックエンド側
Laravelを使用してAPIを作成しました。

ソースコードは下に貼っておきます。

<a href="https://github.com/wataoka/todo-api"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/wataoka/todo-api.png" width="460px"></a>

## ロードマップ
1. 環境構築(composer, mySQL)
2. Laravelでのアプリ新規作成
3. データベース接続
4. モデルの作成
5. マイグレーションの定義
6. マイグレーション
7. コントローラの作成
8. ルートの定義
9. データベースにシードを与える
10. ローカルサーバを立てる

## 1.環境構築
### composer
mac使いなので楽にbrewでinstallした。

```command
brew install composer
```

### MySQL
mac使いなので楽にbrewでinstallした。

```command
brew install mysql
```

## 2. Laravelでのアプリ新規作成

```command
composer create-project laravel/laravel --prefer-dist todo-api
```

最後のtodo-apiの部分はプロジェクト名です。

これを実行すると、でっかいプロジェクトを生成してくれます。ちょっとビビりましたが、適当に解説記事や公式ドキュメントを読みながら把握していきました。

今回はAPIを作るだけですので、viewなど必要のないファイルだらけです。次からはslimやlumenのようなマイクロフレームワークを使おうかなと思いました。

ただ、この時点でかなりの量のLaravelに関する記事やドキュメントを読み通しましたので、このまま突っ走ることにしました。

## 3. データベース接続
今回はMySQLを使用することにしました。

### MySQLコマンドラインツールを起動

```command
mysql -u root
```

### 現在あるデータベースを確認する
```command
mysql> show databases;
```

### 本アプリ用のデータベースを作成し、使用する
```command
mysql> CREATE DATABASE todos;
mysql> use todos;
```

### Laravel側から接続する
.envファイルのDB部分を以下のように変更する。

``` .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=todos
DB_USERNAME=root
DB_PASSWORD=
```

## 4. モデルの作成

```command
php artisan make:model Todo -m
```

これでdatabase/migrations/にxxxx_create_todos_table.phpが作成されます。このファイルにデータベースを作成する設計書を記述していきます。(この時点ではデータベースには何も作成されていない
。)

## 5. マイグレーションの定義

データ構造はとてもシンプルで、

- Todoクラス
  - id (int)
  - title (string)
  - memo (string)
  - status (int)

です。

(Todoを完了したのかどうかをstatusで管理しようとか色々考えていたのですが、結局このうちのmemoしか使っていません。)

このようなデータ構造をデータベースに作成するためにマイグレーションファイルを以下のように編集します。

```xxxx_create_todos_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTodosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->text('memo')->nullable();
            $table->smallInteger('status')->default(0);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('todos');
    }
}
```

## 6. マイグレーションの実行
先ほど作成したマイグレーションファイルを実行します。

```command
php artisan migrate
```

## 7. コントローラの作成
以下のコマンドでコントローラを作成します。

```command
php artisan make:controller TodoController --api --model=Todo
```
これでapp/Http/ControllersにTodoController.phpが作成されています。

そのファイルを編集して、下のようにします。

```php
<?php

namespace App\Http\Controllers;

use App\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Todo::all();
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $data = json_decode(file_get_contents('php://input'), true);

        $todo = new Todo;
        $todo->memo = $data['memo'];
        $todo->save();
        return Todo::all();
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Todo  $todo
     * @return \Illuminate\Http\Response
     */
    public function show(Todo $todo)
    {
        return $todo;
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Todo  $todo
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Todo $todo)
    {
        $todo->title = $request->title;
        $todo->status = $request->status;
        $todo->memo = $request->memo;
        return $todo;
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Todo  $todo
     * @return \Illuminate\Http\Response
     */
    public function destroy(Todo $todo)
    {
        $todo->delete();
    }
}
```

## 8. ルートの定義
さて、さてさて。

Laravelを勉強していて一番感動した所にやっと来ました。ルーティングです。

先ほどのコントローラの部分はartisanで生成した時点で関数を用意してくれていました。その関数の中身を実装するのが僕の役目でした。apiが叩かれた時にどの関数を実行するかを振り分ける作業をするわけですが、元からroutes/api.phpには下のようなコードが書かれています。

```api.php
<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
```

このファイルの一番下に、、、

```php
Route::apiResource('todos', 'TodoController');
```

ヒョイっと一行追加するだけで、

|method|URI|action|
|:--|:--|:--|
|GET|/todos|index|
|GET|/todos/{id}|show|
|POST|/todos|store|
|PUT|/todos/{id}|update|
|DELETE|/todos/{id}|destroy|

というルーティングが定義できるのです！！！

もう一度言います。

僕の行ったことは、

- コントローラを作成する。(コマンドでindex, show, store, update, destroy関数は自動生成してくれる。)
- それぞれの関数を編集する。
- ルーティングに一行加える。

だけです！！！

## 9. データベースにシードを与える
LaravelにはSeederという機能があり、実験的にデータベースにデータを入れたいときに活用できる。

### シーダーファイルを作る

```command
php artisan make:seeder TodosTableSeeder
```

これで`database/seeds`の中に`TodosTableSeeder.php`というファイルができます。そのファイルのrunメソッドの中に流し込むデータを記述します。

### シーダーファイルを編集する

```TodosTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class TodosTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('todos')->insert([
            ['memo' => 'aaaa', 'status' => 0],
            ['memo' => 'bbbb', 'status' => 0],
            ['memo' => 'cccc', 'status' => 0]
        ]);
    }
}
```

ちなみに、runメソッドの一行目に

```php
DB::table('todos')->truncate();
```

と書くと、今までに作ったデータベースを綺麗にすることができます。

### シーダーファイルを実行する

```command
php artisan db:seed --class=TodosTableSeeder
```

これでデータベースにデータが流し込まれたはずです。

## 10. ローカルサーバを立てる
下のコマンドでローカルサーバが立ちます。

```command
php artisan serve
```

適当にブラウザで`http://<localhost名>/api/todos`と打つと、

<img width="806" alt="スクリーンショット 2019-03-21 22.31.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/221435/3bc843f1-74f1-a34d-d4e9-b91476f4570b.png">

しっかりと動いてくれていることが確認できます！

あとはフロント側が作成したものをgit cloneでもして、このapiを叩いてくれれば正常に動きます。

# 最後に
フロントエンドとバックエンドを切り離したWebアプリ作成を作ったことがなかったので、いい練習になりました。かなり楽しかったので、すぐに次のアプリ制作にかかります！今後の僕の成長をどうか見届けてください。


# 参考
- 環境構築
  - [ComposerのインストールからLaravelプロジェクトの作成及び実行まで](https://qiita.com/hirotatsuuu/items/7f86344b1f2ffaf0dc7f)
  - [Mac へ MySQL を Homebrew でインストールする手順](https://qiita.com/hkusu/items/cda3e8461e7a46ecf25d)
- Laravel
  - [Laravel5でJSONリクエストを受け付けるAPIをテストする](https://qiita.com/hosikiti/items/0d4c9cd5bd5b564d0c64)
  - [Laravel5.7: postsのCRUD機能を実装する](https://qiita.com/sutara79/items/4d4854d0f1137aed20d4#%E6%96%B0%E8%A6%8F%E4%BD%9C%E6%88%90-create-store)
  - [HTMLファイルからPOSTでJSONデータを送信する](https://qiita.com/kidatti/items/21cc5c5154dbbb1aa27f)
  - [Laravel5でRequest各種（POST/GET値のやりとり）](https://qiita.com/zaburo/items/b80d4ae73efc99b41541)
  - [https://qiita.com/luccafort/items/0e7c61440c15f86fbfa0](https://qiita.com/luccafort/items/0e7c61440c15f86fbfa0)
  - [Laravelでシーダーを使う](https://qiita.com/shosho/items/b69db263a494edfe3b21)
  - [LaravelでCRUDのAPIをささっと作る](https://qiita.com/sayama0402/items/0188c740360479db3f98)

# 自己紹介

冒頭に書くと邪魔になるので最後にひっそりと自己紹介させてください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/221435/16727490-2b41-4318-cc25-e2601479e77a.jpeg" width=30%>

|名前|綿岡晃輝|
|:--|:--|
|職業|大学院生 (2019年4月から)|
|分野|機械学習, 深層学習, 音声処理|
|Twitter|[@Wataoka_Koki](https://twitter.com/Wataoka_Koki)|

Twitterフォローしてね！