0
1

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 1 year has passed since last update.

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

Last updated at Posted at 2022-04-13

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

このようなTODOアプリを作成していきます。CSSの適用の仕方も書いているので、デザインはご自身でカスタマイズしてみてください。
スクリーンショット 2022-04-13 18.29.04.png

前提

・MAMP環境を利用
・mysqlのPathは設定済み
・データベースは作成済

目次

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

1. プロジェクトフォルダの作成

まず、下記コマンドでプロジェクトフォルダを作成します。
composer create-project "laravel/laravel=8.*" todoapp --prefer-dist

todoappというところがプロジェクトフォルダ名です。

2. データベースの接続先の設定

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

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を変更します。

'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', ''),に書き換えます。

3. マイグレーションファイルの作成

次にテーブルを作成するためのマイグレーションファイルを作成します。
php artisan make:migration create_todos_table

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

4. マイグレーションファイルの編集

下記のようにマイグレーションファイルを編集します。
<?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');
    }
}

この部分でテーブルの構造を定義しています。

    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_atupdated_atのカラムの作成に使われる。

5. マイグレーションの実行

下記コマンドでマイグレーションを実行します。
php artisan migrate

下記コマンドでテーブルを確認すると、先ほどマイグレーションファイルで定義した通りになっています。

mysql> SHOW COLUMNS FROM todos;

確認してみてください。

6. モデルの作成

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

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

php artisan make:model Todo

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

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

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

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

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

7. ルーティングファイルの編集

次にルーティングファイルroutes/web.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;の部分で、参照するコントローラーを指定しています。

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

8. コントローラーの作成

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

php artisan make:controller TodoController

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

<?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('/');
    }
}

●indexアクション

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

●createアクション

あとで説明する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文字'というバリデーションになります。

下記に関しては、

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

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

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

●updateアクション

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

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

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

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

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

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

●deleteアクション

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

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

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

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

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

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

9. bladeファイルの作成

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

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

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

        @error('content')
            <div class="error">{{ $message }}</div>
        @enderror

contentに関するエラーがある場合、エラーメッセージを表示するという記述になります。エスケープ処理のため、Blade上では、変数を{{ }} で囲って記述します。

        <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に代入されている状態となります。

            @foreach($todos as $todo)

            @endforeach

この記述は$todosの中身を一つ一つ、$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>

{{ $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のこの部分です。

    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を表示しています。

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

10. CSSの調整

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

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

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

私が作成した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アプリが完成すると思います。
誰かの参考になれば幸いです。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?