🐘

Laravel入門7 Todoアプリ(CRUD)

に公開

記事一覧

  1. Laravel × Docker 最速環境構築
  2. Laravel 入門1 ページ追加
  3. Laravel入門2 bladeディレクティブ
  4. Laravel入門3 静的ファイル
  5. Laravel入門4 Request
  6. Laravel入門5 Todoアプリ(データベース)
  7. Laravel入門6 Todoアプリ(認証機能)
  8. Laravel入門7 Todoアプリ(CRUD)

CRUD

CRUDとは

以下の頭文字をとったもので、基本的なwebアプリケーションの機能

  • Create(作成)
  • Read(読み取り)
  • Update(更新)
  • Delete(削除)

リレーション設定

関係

1つのUserに対して複数のTaskが紐付いている

リレーションの追加

  1. User.phpを編集
.
├── laravel_app
│   └── app
│       └── Models
│           └── User.php # ここ
├── mysql_data
├── docker-compose.yml
└── Dockerfile
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // リレーションの作成 <- ここを追加
    function tasks() {
        return $this->hasMany(Task::class);
    }
}

タスク作成ページ

web.php

  1. web.php/task/create/task(POST)のルーティングを追加
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TopController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\SignUpController;
use App\Http\Controllers\TaskController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/top', [TopController::class, 'index'])->name("top");

Route::prefix('sign_up')->group(function () {
    Route::get('/', [SignUpController::class, 'index'])->name("sign_up");
    Route::post('/', [SignUpController::class, 'store'])->name("sign_up.store");
});

Route::prefix('login')->group(function () {
    Route::get('/', [LoginController::class, 'index'])->name("login");
    Route::post('/', [LoginController::class, 'store'])->name("login.store");
});

Route::middleware('auth')->group(function () {
    Route::prefix('task')->group(function () {
        Route::get('/', [TaskController::class, 'index'])->name("task");
        // タスク作成ページ
        Route::get('/create', [TaskController::class, 'create'])->name("task.create");
        // タスク作成処理
        Route::post('/', [TaskController::class, 'store'])->name("task.store");
    });
});

Controller

  1. TaskController.phpcreateメソッド追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TaskController extends Controller
{
    function index() {
        return view("task.index");
    }

    // createメソッドを追加
    function create() {
        return view("task.create");
    }
}

View

  1. task/create.blade.phpを作成
.
├── laravel_app
│   └── resources
│       └── views
│           └── task
│               └── create.blade.php # ここに作成
├── mysql_data
├── docker-compose.yml
└── Dockerfile

  1. task/create.blade.phpを記述
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク作成ページ</h1>
    {{-- 【重要】task.storeのルーティングは後で作成する --}}
    <form action={{ route('task.store') }} method="POST">
        @csrf
        <label>タイトル</label>
        <input type="text" name="title">

        <label>内容</label>
        <textarea name="content"></textarea>

        <button type="submit">追加</button>
    </from>

    {{-- タスク一覧ページに戻るボタン --}}
    <a href="{{ route("task") }}">戻る</a>
</body>
</html>

http://localhost:8000/task/createにアクセスして以下のような画面が出ればOK

タスク作成処理

Controller

  1. TaskController.phpstoreメソッドを追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; // ログイン情報取得に使用

class TaskController extends Controller
{
    function index() {
        return view("task.index");
    }

    function create() {
        return view("task.create");
    }

    // storeメソッドを追加
    function store(Request $request) {
        // それぞれの入力値を取得
        $title = $request["title"];
        $content = $request["content"];

        // Atuh::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを作成
        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        // タスク一覧画面にリダイレクト
        return redirect()->route("task");
    }
}

http://localhost:8000/task/createからタスクを追加したときにタスク一覧ページにリダイレクトすればOK

タスク一覧ページ

Controller

1 TopController.phpindexメソッドを編集

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class TaskController extends Controller
{
    function index() {
        // Atuh::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを取得
        $tasks = $user->tasks;
        return view("task.index", compact("tasks"));
    }

    function create() {
        return view("task.create");
    }

    function store(Request $request) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();


        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }
}

View

  1. task/index.blade.phpに一覧テーブルを追加
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク一覧ページ</h1>
    {{-- 一覧テーブルを作成 --}}
    <table>
        <tr>
            <th>タイトル</th>
            <th>内容</th>
            <th>作成日時</th>
            <th>更新日時</th>
        </tr>

        {{-- コントローラーから受け取った$tasksをループして表示 --}}
        @foreach ($tasks as $task)
        <tr>
            <td>{{ $task["title"] }}</td>
            <td>{{ $task["content"] }}</td>
            <td>{{ $task["created_at"] }}</td>
            <td>{{ $task["updated_at"] }}</td>
        </tr>
        @endforeach
    </table>

    {{-- タスク作成ページに遷移するボタン --}}
    <a href="{{ route("task.create") }}">タスク作成</a>
</body>
</html>

http://localhost:8000/taskにアクセスして以下のような画面が出ればOK

タスク詳細ページ

web.php

  1. web.php/task/{id}のルーティングを追加
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TopController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\SignUpController;
use App\Http\Controllers\TaskController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/top', [TopController::class, 'index'])->name("top");

Route::prefix('sign_up')->group(function () {
    Route::get('/', [SignUpController::class, 'index'])->name("sign_up");
    Route::post('/', [SignUpController::class, 'store'])->name("sign_up.store");
});

Route::prefix('login')->group(function () {
    Route::get('/', [LoginController::class, 'index'])->name("login");
    Route::post('/', [LoginController::class, 'store'])->name("login.store");
});

Route::middleware('auth')->group(function () {
    Route::prefix('task')->group(function () {
        Route::get('/', [TaskController::class, 'index'])->name("task");
        Route::get('/create', [TaskController::class, 'create'])->name("task.create");
        Route::post('/', [TaskController::class, 'store'])->name("task.store");
        // タスク詳細ページ
        // 波括弧で囲んだ値は、任意の文字になる
        // 例:/task/1, /task/2 などでアクセスできる
        Route::get('/{id}', [TaskController::class, 'show'])->name("task.show");
    });
});

Controller

  1. TaskController.phpshowメソッドを追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class TaskController extends Controller
{
    function index() {
        $user = Auth::user();

        $tasks = $user->tasks;
        return view("task.index", compact("tasks"));
    }

    function create() {
        return view("task.create");
    }

    function store(Request $request) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();


        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }

    // ルーティングの{id}は第一引数に入る
    function show($id) {
        // Atuh::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを取得
        // $idとtasksテーブルのidが一致するものを取得
        $task = $user->tasks->find($id);

        return view("task.show", compact("task"));
    }
}

View

  1. task/show.blade.phpを作成
.
├── laravel_app
│   └── resources
│       └── views
│           └── task
│               └── show.blade.php # ここに作成
├── mysql_data
├── docker-compose.yml
└── Dockerfile
  1. task/show.blade.phpを記述
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク詳細ページ</h1>

    {{-- コントローラーから受け取った$taskを表示 --}}
    <p>タイトル: {{ $task["title"] }}</p>
    <p>内容: {{ $task["content"] }}</p>
    <p>作成日時: {{ $task["created_at"] }}</p>
    <p>更新日時: {{ $task["updated_at"] }}</p>

    {{-- タスク一覧ページに戻るボタン --}}
    <a href="{{ route("task") }}">戻る</a>
</body>
</html>

  1. task/index.blade.phpに詳細ページに飛ぶリンクを追加
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク一覧ページ</h1>
    <table>
        <tr>
            <th>タイトル</th>
            <th>内容</th>
            <th>作成日時</th>
            <th>更新日時</th>
            <th></th>
        </tr>

        @foreach ($tasks as $task)
        <tr>
            <td>{{ $task["title"] }}</td>
            <td>{{ $task["content"] }}</td>
            <td>{{ $task["created_at"] }}</td>
            <td>{{ $task["updated_at"] }}</td>
            <td>
                {{-- 詳細ページに遷移するリンクを作成 --}}
                <a href={{ route("task.show", ["id" => $task["id"]]) }}>詳細</a>
            </td>
        </tr>
        @endforeach
    </table>

    <a href="{{ route("task.create") }}">タスク作成</a>
</body>
</html>

http://localhost:8000/taskから詳細ページにアクセスして以下のような画面が出ればOK

タスク編集ページ

web.php

  1. web.php/task/{id}/edittask/{id}(PUT)のルーティングを追加
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TopController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\SignUpController;
use App\Http\Controllers\TaskController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/top', [TopController::class, 'index'])->name("top");

Route::prefix('sign_up')->group(function () {
    Route::get('/', [SignUpController::class, 'index'])->name("sign_up");
    Route::post('/', [SignUpController::class, 'store'])->name("sign_up.store");
});

Route::prefix('login')->group(function () {
    Route::get('/', [LoginController::class, 'index'])->name("login");
    Route::post('/', [LoginController::class, 'store'])->name("login.store");
});

Route::middleware('auth')->group(function () {
    Route::prefix('task')->group(function () {
        Route::get('/', [TaskController::class, 'index'])->name("task");
        Route::get('/create', [TaskController::class, 'create'])->name("task.create");
        Route::post('/', [TaskController::class, 'store'])->name("task.store");
        Route::get('/{id}', [TaskController::class, 'show'])->name("task.show");
        // タスク編集ページ
        Route::get('/{id}/edit', [TaskController::class, 'edit'])->name("task.edit");
        // タスク編集処理
        Route::put('/{id}', [TaskController::class, 'update'])->name("task.update");
    });
});

Controller

  1. TaskController.phpeditメソッドを追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class TaskController extends Controller
{
    function index() {
        $user = Auth::user();

        $tasks = $user->tasks;
        return view("task.index", compact("tasks"));
    }

    function create() {
        return view("task.create");
    }

    function store(Request $request) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();


        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }

    function show($id) {
        $user = Auth::user();

        $task = $user->tasks->find($id);

        return view("task.show", compact("task"));
    }

    // editメソッドを追加
    function edit($id) {
        // Atuh::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを取得
        // $idとtasksテーブルのidが一致するものを取得
        $task = $user->tasks->find($id);

        return view("task.edit", compact("task"));
    }
}

View

  1. task/edit.blade.phpを作成
.
├── laravel_app
│   └── resources
│       └── views
│           └── task
│               └── edit.blade.php # ここに作成
├── mysql_data
├── docker-compose.yml
└── Dockerfile
  1. task/edit.blade.phpを記述
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク編集ページ</h1>

    {{-- 【重要】task.updateのルーティングは後で作成する --}}
    <form action={{ route('task.update', ['id' => $task["id"]]) }} method="POST">
        @csrf
        {{-- 【重要】HTTPメソッドをPUTに変更 --}}
        @method('PUT')

        <label>タイトル</label>
        {{-- value属性に$task["title"]を設定することで、編集前のタイトルが入力値に入る --}}
        <input type="text" name="title" value="{{ $task["title"] }}">

        <label>内容</label>
        {{-- textarea内に$task["content"]を設定することで、編集前の内容が入力値に入る --}}
        <textarea name="content">{{ $task["content"] }}</textarea>

        <button type="submit">更新</button>
    </form>

    {{-- タスク一覧ページに戻るボタン --}}
    <a href="{{ route("task.show", ["id" => $task["id"]]) }}">戻る</a>
</body>
</html>

  1. task/show.blade.phpに編集ページに遷移するリンクを追加
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク詳細ページ</h1>

    <p>タイトル: {{ $task["title"] }}</p>
    <p>内容: {{ $task["content"] }}</p>
    <p>作成日時: {{ $task["created_at"] }}</p>
    <p>更新日時: {{ $task["updated_at"] }}</p>

    <a href="{{ route("task") }}">戻る</a>
    {{-- 編集ページに遷移するリンクを追加 --}}
    <a href="{{ route("task.edit", ["id" => $task["id"]]) }}">編集</a>
</body>
</html>

http://localhost:8000/taskから詳細ページにアクセスして編集ページを開き、以下のような画面になっていればOK

タスク編集処理

Controller

  1. TaskController.phpupdateメソッドを追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class TaskController extends Controller
{
    function index() {
        $user = Auth::user();

        $tasks = $user->tasks;
        return view("task.index", compact("tasks"));
    }

    function create() {
        return view("task.create");
    }

    function store(Request $request) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();


        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }

    function show($id) {
        $user = Auth::user();

        $task = $user->tasks->find($id);

        return view("task.show", compact("task"));
    }

    function edit($id) {
        $user = Auth::user();

        $task = $user->tasks->find($id);

        return view("task.edit", compact("task"));
    }

    // updateメソッドを追加
    function update(Request $request, $id) {
        // それぞれの入力値を取得
        $title = $request["title"];
        $content = $request["content"];

        // Auth::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを取得
        // $idとtasksテーブルのidが一致するものを取得
        // updateメソッドで更新
        $user->tasks()->find($id)->update([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }
}

http://localhost:8000/taskから詳細ページにアクセスして編集ページを開き"更新"を押した際に更新され、一覧ページに戻ればOK

タスク削除処理

web.php

  1. web.php/task(DELETE)のルーティングを追加
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TopController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\SignUpController;
use App\Http\Controllers\TaskController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/top', [TopController::class, 'index'])->name("top");

Route::prefix('sign_up')->group(function () {
    Route::get('/', [SignUpController::class, 'index'])->name("sign_up");
    Route::post('/', [SignUpController::class, 'store'])->name("sign_up.store");
});

Route::prefix('login')->group(function () {
    Route::get('/', [LoginController::class, 'index'])->name("login");
    Route::post('/', [LoginController::class, 'store'])->name("login.store");
});

Route::middleware('auth')->group(function () {
    Route::prefix('task')->group(function () {
        Route::get('/', [TaskController::class, 'index'])->name("task");
        Route::get('/create', [TaskController::class, 'create'])->name("task.create");
        Route::post('/', [TaskController::class, 'store'])->name("task.store");
        Route::get('/{id}', [TaskController::class, 'show'])->name("task.show");
        Route::get('/{id}/edit', [TaskController::class, 'edit'])->name("task.edit");
        Route::put('/{id}', [TaskController::class, 'update'])->name("task.update");
        // タスク削除処理
        Route::delete('/{id}', [TaskController::class, 'destroy'])->name("task.destroy");
    });
});

Controller

  1. TaskController.phpdestroyメソッドを追加
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class TaskController extends Controller
{
    function index() {
        $user = Auth::user();

        $tasks = $user->tasks;
        return view("task.index", compact("tasks"));
    }

    function create() {
        return view("task.create");
    }

    function store(Request $request) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();


        $user->tasks()->create([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }

    function show($id) {
        $user = Auth::user();

        $task = $user->tasks->find($id);

        return view("task.show", compact("task"));
    }

    function edit($id) {
        $user = Auth::user();

        $task = $user->tasks->find($id);

        return view("task.edit", compact("task"));
    }

    function update(Request $request, $id) {
        $title = $request["title"];
        $content = $request["content"];

        $user = Auth::user();

        $user->tasks()->find($id)->update([
            "title" => $title,
            "content" => $content
        ]);

        return redirect()->route("task");
    }

    // destroyメソッドを追加
    function destroy($id) {
        // Atuh::user()でログインしているユーザーの情報を取得
        $user = Auth::user();

        // tasksリレーションを使用してユーザーに紐づくタスクを取得
        // $idとtasksテーブルのidが一致するものを取得
        // deleteメソッドで削除
        $user->tasks()->find($id)->delete();

        return redirect()->route("task");
    }
}

View

  1. task/show.blade.phpに削除処理を行うフォームを作成
<!DOCTYPE html>
<html lang="en">
<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">
    <title>Document</title>
</head>
<body>
    <h1>タスク詳細ページ</h1>

    <p>タイトル: {{ $task["title"] }}</p>
    <p>内容: {{ $task["content"] }}</p>
    <p>作成日時: {{ $task["created_at"] }}</p>
    <p>更新日時: {{ $task["updated_at"] }}</p>

    <a href="{{ route("task") }}">戻る</a>

    <a href="{{ route("task.edit", ["id" => $task["id"]]) }}">編集</a>

    {{-- 削除処理を行うフォームを作成 --}}
    <form action={{ route('task.destroy', ['id' => $task["id"]]) }} method="POST">
        @csrf
        {{-- 【重要】HTTPメソッドをDELETEに変更 --}}
        @method('DELETE')
        <button type="submit">削除する</button>
    </form>
</body>
</html>

http://localhost:8000/taskから詳細ページを開き"削除する"を押した際にタスクが削除され、一覧ページに戻ればOK

Discussion