Laravel初心者がTODOアプリを作成したときのまとめ 

Laravel初心者の私がTODOアプリを作成した際の手順をまとめてみました。
*ベストなやり方ではないかもしれないのでご了承ください。

このようなTODOアプリを作成していきます。CSSの適用の仕方も書いているので、デザインはご自身でカスタマイズしてみてください。
![スクリーンショット 2022-04-13 18.29.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627190/602b0d2b-627e-c0e4-a9fe-5b41b2810466.png)


<h2>前提</h2>
・MAMP環境を利用<br>
・mysqlのPathは設定済み<br>
・データベースは作成済

<h2>目次</h2>
1. プロジェクトフォルダの作成<br>
2. データベースの接続先の設定<br>
3. マイグレーションファイルの作成<br>
4. マイグレーションファイルの編集<br>
5. マイグレーションの実行<br>
6. モデルの作成<br>
7. ルーティングファイルの編集<br>
8. コントローラーの作成<br>
9. bladeファイルの作成<br>
10. CSSの調整


<h2>1. プロジェクトフォルダの作成</h2>
まず、下記コマンドでプロジェクトフォルダを作成します。

```
composer create-project "laravel/laravel=8.*" todoapp --prefer-dist
```
`todoapp`というところがプロジェクトフォルダ名です。

<h2>2. データベースの接続先の設定</h2>

プロジェクト直下の`.env`ファイルを開き、データベースの接続先を設定します。

```csharp
DB_CONNECTION=mysql
DB_UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=(データベース名)
DB_USERNAME=root
DB_PASSWORD=（パスワード）
```

`DB_UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock`に関しては、接続できなかった場合に試してみてください。

次に`config/database.php`を変更します。

```php5
'mysql' => [
    'driver' => 'mysql',
    'url' => env('DATABASE_URL'),
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => env('DB_DATABASE', 'forge'),
    'username' => env('DB_USERNAME', 'forge'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_UNIX_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        ]) : [],
    ],
```

`DB_UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock`を追加した場合、`'unix_socket' => env('DB_SOCKET', ''),`を`'unix_socket' => env('DB_UNIX_SOCKET', ''),`に書き換えます。

<h2>3. マイグレーションファイルの作成</h2>
次にテーブルを作成するためのマイグレーションファイルを作成します。

```
php artisan make:migration create_todos_table
```
`database/migrations`の中に`xxxx_xx_xx_xxxxxx_create_todos_table`というマイグレーション ファイルが作成されます。

<h2>4. マイグレーションファイルの編集</h2>
下記のようにマイグレーションファイルを編集します。

```php5
<?php

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

class CreateTodosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->id()->nullable(false);
            $table->string('content', 191)->nullable(false);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('todos');
    }
}
```
この部分でテーブルの構造を定義しています。

```php5
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->id()->nullable(false);
            $table->string('content', 191)->nullable(false);
            $table->timestamps();
        });
    }
```

`nullable(false)`　→ 値が入っていることが必須という意味。
`string('content', 191)` → `string('カラム名', 長さ)`という意味。
`timestamps()`　→ `created_at`と`updated_at`のカラムの作成に使われる。

<h2>5. マイグレーションの実行</h2>
下記コマンドでマイグレーションを実行します。

```
php artisan migrate
```
下記コマンドでテーブルを確認すると、先ほどマイグレーションファイルで定義した通りになっています。
```
mysql> SHOW COLUMNS FROM todos;
```
確認してみてください。

<h2>6. モデルの作成</h2>

Laravelには、Eloquent（エロクアント）というORMが搭載されています。Laravelではクラス（モデル）を用意して操作すると、対応するデータベースを自動的に操作することができます。

モデルを作成することで、後ほど説明する`TodoController.php`で簡単にデータベースの操作を行うことができるようになります。

```
php artisan make:model Todo
```

これでモデルが作成できます。テーブル名は複数形、モデル名は単数形という決まりがあります。今回は`todos`というテーブルだったので、単数形の`Todo`というモデルを作成しました。これで、モデルとテーブルが紐づきます。

プロジェクトフォルダの`app>Models`フォルダの中を確認してみましょう。そこに`Todo.php`というファイルが作成されています。


今回は`Todo.php`の中身は特に変更しなくても大丈夫ですが、モデルは、

①データ構造とそれを操作する全て(処理、検証、保存など)
②データ層へのアクセス

などに使うファイルとのことです。

<h2>7. ルーティングファイルの編集</h2>

次にルーティングファイル`routes/web.php`を編集します。

```php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TodoController;

Route::get('/', [TodoController::class, 'index']);
Route::post('/create', [TodoController::class, 'create']);
Route::post('/update', [TodoController::class, 'update'])->name('todo.update');
Route::post('/delete', [TodoController::class, 'delete'])->name('todo.delete');
```

どこにアクセスしたら、どのアクションが実行されるのかを定義していきます。

今回は下記を定義します。

・`トップページ(/)`にアクセス　→ コントローラーに書かれた`indexアクション`を実行。データベースの中身を取得し、ページに表示させます。
・`レコード作成(/create)`にアクセス　→ コントローラーに書かれた`createアクション`を実行。テーブルに新しいレコードを作成し、トップページにリダイレクトさせます。
・`レコード更新(/update)`にアクセス　→ コントローラーに書かれた`updateアクション`を実行。レコードの内容を更新し、トップページにリダイレクトさせます。
・`レコード削除(/delete)`にアクセス　→ コントローラーに書かれた`deleteアクション`を実行。レコードを物理削除し、トップページにリダイレクトさせます。

また、`use App\Http\Controllers\TodoController;`の部分で、参照するコントローラーを指定しています。

ちなみに、ルートには名前を付けることができるのですが、`update`と`delete`にはそれぞれ下記のように名前を付けています。
`->name('todo.update')`
`->name('todo.delete')`

<h2>8. コントローラーの作成</h2>

下記コマンドを実行し、todoコントローラーを作成します。

```
php artisan make:controller TodoController
```

`app/Http/Controllers/TodoController.php`が作成されました。

```php5
<?php

namespace App\Http\Controllers;

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

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::all();
        return view('index', ['todos' => $todos]);
    }

    public function create(Request $request)
    {
        $request->validate([
            'new_content' => 'required|min:3|max:20'
        ], [
            'new_content.required' => '必須項目です！',
            'new_content.min' => ':min 文字以上入力してください。',
            'new_content.max' => ':max 文字以下で入力しください。'
        ]);

        $post = new Todo();
        $post->content = $request->new_content;
        $post->save();
        return redirect('/');
    }

    public function update(Request $request)
    {
        $request->validate([
            'content' => 'required|min:3|max:20'
        ], [
            'content.required' => '必須項目です！',
            'content.min' => ':min 文字以上入力してください。',
            'content.max' => ':max 文字以下で入力しください。'
        ]);

        $form = $request->all();
        unset($form['_token']);

        $update = Todo::find($request->id);
        $update->content = $request->content;
        $update->save();
        return redirect('/');
    }

    public function delete(Request $request)
    {
        $delete = Todo::find($request->id);
        $delete->delete();

        return redirect('/');
    }
}
```
<h3>●indexアクション</h3>

`$todos = Todo::all();`の部分でtodoテーブルの全ての情報を取得し、`$todos`に代入しています。
`return view('index', ['todos' => $todos]);`の部分はindex.blade.phpを表示するという意味で、先ほど代入したテーブルの中身`$todos`をindex.blade.phpで使うために、`todos`にもう一度代入しています。
`$todos`のまま送れるのでは？と思いましたが、bladeファイルで使うためには`todos`に代入する必要があるようです。

<h3>●createアクション</h3>

あとで説明する`index.blade.php`のインプットフォームから送られてきた値は、`$request`に代入されています。また、`$request->validate`ではバリデーションを定義することができます。

`'new_content' => 'required|min:3|max:20'`この部分の`'new_content'`は`index.blade.php`のインプットフォームの`name属性`です。
`'required|min:3|max:20'`は`'必須項目|ミニマム3文字|マックス20文字'`というバリデーションになります。

下記に関しては、

```php5
$post = new Todo();
$post->content = $request->new_content;
$post->save();
return redirect('/');
```

①モデル`todo.php`からインスタンスを生成。
②contentカラムに`new_content`の中身を代入。
③その情報をtodosテーブルに保存。
④`(/)`にリダイレクト。

という流れになっています。

<h3>●updateアクション</h3>

`$form = $request->all();`はリクエストの全データを取得して、`$form`に代入するという意味です。インプットフォームでは文字しか入力できませんが、実際は色々な情報が送られています。変数の中身を確認するには、`dd($form);`をソースコードに追加して、ページにアクセスしてみてください。

また、`unset($form['_token']);`でフォームから送られてきた_tokenを削除しています。`index.blade.php`からcsrf対策用のトークンが送られて来るのですが、データベースに保存するときに不要なので、ここで削除しています。

下記に関しては、createアクションとほぼ同じですが、

```php5
$update = Todo::find($request->id);
$update->content = $request->content;
$update->save();
return redirect('/');
```

①モデル`todo.php`を利用して、todosテーブルから`$request`のIDに一致するものを取得。
②contentカラムに`content`の中身を代入。
③その情報をtodosテーブルに保存。
④`(/)`にリダイレクト。

という流れになっています。

<h3>●deleteアクション</h3>

レコードを削除するためのアクションですが、

```php5
$delete = Todo::find($request->id);
$delete->delete();
```

①モデル`todo.php`を利用して、todosテーブルから`$request`のIDに一致するものを取得。
②IDが一致するレコードを削除。
③`(/)`にリダイレクト。

という流れになっています。

ーーーーーーーーーーーーーーーーーーーーーー

ちなみに、コントローラーで上記のデータベース操作の構文を使うためには、`use App\Models\Todo;`を記述する必要があります。



<h2>9. bladeファイルの作成</h2>

`resources/views/`の直下に`index.blade.php`を作成します。これはWebページを作成するときの`index.html`のようなものです。

```csharp
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link href="{{ asset('css/style.css') }}" rel="stylesheet">
    <title>Document</title>
</head>
<body>
    <div class="container">
        <h1>TODOアプリ</h1>
        @error('content')
            <div class="error">{{ $message }}</div>
        @enderror
        @error('new_content')
            <div class="error">{{ $message }}</div>
        @enderror
        <form action="create" method="post" class="addForm">
            @csrf
            {{-- <label for="content">TODO</label> --}}
            <input type="text" id="content" name="new_content" class="addInput" value = "{{ old('new_content') }}">
            <input type="submit" value="追加" class="addButton">
        </form>

        <table>
            <tr class="tableHeader">
                <th class="headerCreateDate">作成日</th>
                <th class="headerTaskName">タスク名</th>
                <th class="headerUpdate">更新</th>
                <th class="headerDelete">削除</th>
            </tr>

            @foreach($todos as $todo)
            <tr>
                <td class="itemCreateDate">
                    {{ $todo->updated_at }}
                </td>

                <form action="{{ route('todo.update', ['id' => $todo->id]) }}" method="post">
                    @csrf
                    <td>
                        <input type="text" name="content" value="{{ $todo->content }}" class="updateInput">
                    </td>
                    <td>
                        <input type="submit" value="追加" class="updateButton">
                    </td>
                </form>

                <td>
                    <form action="{{ route('todo.delete', ['id' => $todo->id]) }}" method="post">
                        @csrf
                        <input type="submit" value="削除" class="deleteButton">
                    </form>
                </td>
            </tr>
            @endforeach
        </table>
    </div>
</body>
</html>
```

上から順番に見ていきます。

```php5
        @error('content')
            <div class="error">{{ $message }}</div>
        @enderror
```
`content`に関するエラーがある場合、エラーメッセージを表示するという記述になります。エスケープ処理のため、Blade上では、変数を`{{ }} `で囲って記述します。

```php5
        <form action="create" method="post" class="addForm">
            @csrf
            <input type="text" id="content" name="new_content" class="addInput" value = "{{ old('new_content') }}">
            <input type="submit" value="追加" class="addButton">
        </form>
```

`<form action="create" method="post" class="addForm">`この部分で、フォームを送信したら、`ルーティングファイル(web.php)`のcreateにアクセスし、`コントローラー（TodoController.php）`のcreateアクションが実行されることになります。

また、ここでインプットフォームに入力された値は、`name="new_content"`などの情報と一緒に送信され、`$request`に代入されている状態となります。

```php5
            @foreach($todos as $todo)

            @endforeach
```
この記述は`$todos`の中身を一つ一つ、`$todo`に入れて繰り返すという意味となります。


```php5
            <tr>
                <td class="itemCreateDate">
                    {{ $todo->updated_at }}
                </td>

                <form action="{{ route('todo.update', ['id' => $todo->id]) }}" method="post">
                    @csrf
                    <td>
                        <input type="text" name="content" value="{{ $todo->content }}" class="updateInput">
                    </td>
                    <td>
                        <input type="submit" value="追加" class="updateButton">
                    </td>
                </form>

                <td>
                    <form action="{{ route('todo.delete', ['id' => $todo->id]) }}" method="post">
                        @csrf
                        <input type="submit" value="削除" class="deleteButton">
                    </form>
                </td>
            </tr>
```

`{{ $todo->updated_at }}`は先ほど`foreach`で`$todo`に代入されたレコードの中身の`updated_atカラム`の値という意味です。この記述でレコードの作成日が表示されます。

`@csrf`はCSRF（クロスサイト・リクエスト・フォージェリ）対策のための記述です。Laravelでは、`@csrf`と記述することでCSRF対策を行うことができます。また、これがないとエラーになってしまうので、インプットフォームを使う際は必ず記入しましょう。


`<form action="{{ route('todo.update', ['id' => $todo->id]) }}" method="post">`は、`route('todo.update')`の部分で、ルーティングで命名した`todo.update`にアクセスしています。

また、`id`に`$todo`に代入されたレコードの中身のidカラムの値が代入され、これがルーティングを経由して、コントローラーの`updateアクション`に送られます。

`TodoController.php`のこの部分です。

```php5
    public function update(Request $request)
    {
        $request->validate([
            'content' => 'required|min:3|max:20'
        ], [
            'content.required' => '必須項目です！',
            'content.min' => ':min 文字以上入力してください。',
            'content.max' => ':max 文字以下で入力しください。'
        ]);

        $form = $request->all();
        unset($form['_token']);

        $update = Todo::find($request->id);
        $update->content = $request->content;
        $update->save();

        return redirect('/');
    }
```

`{{ $todo->content }}`の部分は`{{ $todo->updated_at }}`を表示したときと同じ処理で、インプットフォームの中に既存のレコードの`content`を表示しています。

その下の削除の処理は、ほとんど同じ流れなので割愛します。


<h2>10. CSSの調整</h2>

ここまででTODOアプリの処理は一応完成したので、次にデザインを整えるため、CSSを適用していきます。

CSSファイルは、`public/css`の直下に配置します。また、今回はファイル名を`style.css`にしているので、これを利用するためには`index.blade.php`に下記を追加します。

```php5
<link href="{{ asset('css/style.css') }}" rel="stylesheet">
```

私が作成したCSSファイルは下記ですがもっと良い感じにカスタマイズしてみてください。

```css
/* 共通 */

body {
    background-color: purple;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

.container {
    width: 60%;
    background-color: white;
    padding: 40px;
    border-radius: 10px;
}

.error {
    margin-bottom: 15px;
    background-color: darkgrey;
    width: 30%;
    text-align: center;
}

/* 追加フォーム */

.addForm {
    display: flex;
    justify-content: space-between;
    margin-bottom: 30px;
}

.addInput {
    width: 80%;
    height: 35px;
}

.addButton {
    width: 10%;
    color: blueviolet;
    background-color: white;
    border-color: blueviolet;
    border-radius: 5px;
}

/* 更新削除フォーム */

table {
    width: 100%;
}

th {
    text-align: center;
}

td {
    text-align: center;
}

.itemCreateDate {
    width: 30%;
}

.updateInput {
    height: 25px;
    width: 80%;
}

.updateButton {
    color: chocolate;
    background-color: white;
    border-color: chocolate;
    border-radius: 5px;
    border-style: solid;
    height: 40px;
    width: 70%;
    margin-right: auto;
}

.deleteButton {
    color: rgb(146, 252, 211);
    background-color: white;
    border-color: rgb(146, 252, 211);
    border-radius: 5px;
    border-style: solid;
    height: 40px;
    width: 70%;
}
```

これでTODOアプリが完成すると思います。
誰かの参考になれば幸いです。