CRUDアプリを完成させる
いよいよ最終回です。ルーティング・コントローラー・ビュー・Eloquent・バリデーション——これまで学んだすべてを使ってタスク管理アプリの CRUD? データ操作の基本4種「Create(作成)・Read(取得)・Update(更新)・Delete(削除)」の頭文字。Webアプリの基本機能。 を完成させましょう。
作るもの
シンプルなタスク管理アプリです。次の7つの画面と操作を実装します。
| 機能 | URL | 方法 | コントローラーのメソッド |
|---|---|---|---|
| 一覧を見る | /tasks | GET | index |
| 追加フォームを表示 | /tasks/create | GET | create |
| 追加する | /tasks | POST | store |
| 詳細を見る | /tasks/{id} | GET | show |
| 編集フォームを表示 | /tasks/{id}/edit | GET | edit |
| 更新する | /tasks/{id} | PUT | update |
| 削除する | /tasks/{id} | DELETE | destroy |
① ルートをまとめて登録する
7つのルートを1行で登録できます。routes/web.php を開き、既存の /tasks/create と /tasks のルートを削除して、次の1行に置き換えます。
<?php
// 📁 routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController;
Route::get('/', function () {
return view('welcome');
});
Route::resource('tasks', TaskController::class); // ← この1行で7つのルートが登録される
Route::resource() は上の表の7ルートをすべて自動で登録します。
② リソースコントローラーを作る
php artisan make:controller TaskController --resource
--resource フラグをつけると、7つのメソッド(index・create・store・show・edit・update・destroy)があらかじめ書かれた リソースコントローラー? CRUD操作(一覧・詳細・作成・更新・削除)に対応した7つのメソッドをまとめたコントローラー。`Route::resource()`と組み合わせて使う。 が生成されます。
app/Http/Controllers/TaskController.php を開くとこのようになっています。
<?php
// 📁 app/Http/Controllers/TaskController.php(自動生成直後)
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TaskController extends Controller
{
public function index() { /* 一覧 */ }
public function create() { /* 作成フォーム */ }
public function store(Request $request) { /* 保存 */ }
public function show(string $id) { /* 詳細 */ }
public function edit(string $id) { /* 編集フォーム */ }
public function update(Request $request, string $id) { /* 更新 */ }
public function destroy(string $id) { /* 削除 */ }
}
これに処理を埋めていきます。
③ 一覧ページ(index)
コントローラー
TaskController.php の index() を書き換えます。
// 📁 app/Http/Controllers/TaskController.php
// index() メソッドを書き換える
use App\Models\Task; // ← クラスの直前に追加
public function index()
{
$tasks = Task::orderBy('created_at', 'desc')->get();
// orderBy('created_at', 'desc') → 新しい順
return view('tasks.index', compact('tasks'));
// compact('tasks') は ['tasks' => $tasks] と同じ意味
}
ビュー
resources/views/tasks/index.blade.php を新規作成します。
<!-- 📁 resources/views/tasks/index.blade.php(新規作成) -->
@extends('layouts.app')
@section('title', 'タスク一覧')
@section('content')
<div>
<h1>タスク一覧</h1>
<a href="{{ route('tasks.create') }}">+ タスクを追加</a>
</div>
@if (session('success'))
<p>{{ session('success') }}</p>
@endif
@forelse ($tasks as $task)
<div>
<span>{{ $task->done ? '✅' : '⬜' }}</span>
<a href="{{ route('tasks.show', $task->id) }}">{{ $task->title }}</a>
</div>
@empty
<p>タスクはまだありません。</p>
@endforelse
@endsection
route('tasks.create') は /tasks/create のURLを生成します。Route::resource() で登録したルートには自動的に名前がつきます(tasks.index・tasks.create・tasks.show など)。
④ 作成フォームと保存(create / store)
コントローラー
// 📁 app/Http/Controllers/TaskController.php
// create() と store() を書き換える
public function create()
{
return view('tasks.create');
// resources/views/tasks/create.blade.php を表示
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => ['required', 'max:100'],
'body' => ['nullable', 'max:1000'],
]);
Task::create([
'title' => $validated['title'],
'body' => $validated['body'] ?? null,
'done' => false,
]);
return redirect()->route('tasks.index')
->with('success', 'タスクを追加しました');
}
redirect()->route('tasks.index') は「tasks.index という名前のルート(= /tasks)にリダイレクトする」という意味です。
ビュー
resources/views/tasks/create.blade.php を新規作成します。
<!-- 📁 resources/views/tasks/create.blade.php(新規作成) -->
@extends('layouts.app')
@section('title', 'タスクを追加')
@section('content')
<h1>タスクを追加</h1>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
<form method="POST" action="{{ route('tasks.store') }}">
@csrf
<div>
<label for="title">タイトル(必須)</label>
<input type="text" id="title" name="title" value="{{ old('title') }}">
@error('title') <p>{{ $message }}</p> @enderror
</div>
<div>
<label for="body">内容</label>
<textarea id="body" name="body">{{ old('body') }}</textarea>
</div>
<button type="submit">追加する</button>
<a href="{{ route('tasks.index') }}">キャンセル</a>
</form>
@endsection
action="{{ route('tasks.store') }}" はフォームの送信先URLです。/tasks(POST)になります。
⑤ 詳細ページ(show)
コントローラー
// 📁 app/Http/Controllers/TaskController.php
// show() を書き換える
public function show(Task $task)
// ↑ 型ヒントをつけると自動でDBから取得してくれる
{
return view('tasks.show', compact('task'));
}
引数を string $id から Task $task に変えました。URLの {task} パラメーターとモデルの型ヒントを組み合わせることで、Laravelが自動的に Task::findOrFail($id) を実行してくれます(ルートモデルバインディングといいます)。
ビュー
resources/views/tasks/show.blade.php を新規作成します。
<!-- 📁 resources/views/tasks/show.blade.php(新規作成) -->
@extends('layouts.app')
@section('title', $task->title)
@section('content')
<h1>{{ $task->title }}</h1>
@if ($task->body)
<p>{{ $task->body }}</p>
@endif
<p>状態: {{ $task->done ? '完了' : '未完了' }}</p>
<div>
{{-- 編集ページへのリンク --}}
<a href="{{ route('tasks.edit', $task->id) }}">編集</a>
{{-- 削除ボタン(フォームで DELETE を送信) --}}
<form method="POST" action="{{ route('tasks.destroy', $task->id) }}">
@csrf
@method('DELETE')
<button type="submit" onclick="return confirm('削除しますか?')">削除</button>
</form>
</div>
<a href="{{ route('tasks.index') }}">← 一覧に戻る</a>
@endsection
@method('DELETE') は重要です。HTMLの <form> はGETとPOSTしか送れません。@method('DELETE') を書くと、LaravelがPOSTリクエストの中から「これはDELETE操作だ」と判断してくれます。PUT・PATCH・DELETE を使うときは必ず必要です。
⑥ 編集フォームと更新(edit / update)
コントローラー
// 📁 app/Http/Controllers/TaskController.php
// edit() と update() を書き換える
public function edit(Task $task)
{
return view('tasks.edit', compact('task'));
}
public function update(Request $request, Task $task)
{
$validated = $request->validate([
'title' => ['required', 'max:100'],
'body' => ['nullable', 'max:1000'],
'done' => ['boolean'],
]);
$task->update($validated);
// $validated の内容でまとめて更新
return redirect()->route('tasks.show', $task->id)
->with('success', 'タスクを更新しました');
}
ビュー
resources/views/tasks/edit.blade.php を新規作成します。
<!-- 📁 resources/views/tasks/edit.blade.php(新規作成) -->
@extends('layouts.app')
@section('title', '編集: ' . $task->title)
@section('content')
<h1>タスクを編集</h1>
<form method="POST" action="{{ route('tasks.update', $task->id) }}">
@csrf
@method('PUT')
{{-- PUT メソッドを使う(HTMLフォームでは @method('PUT') が必要) --}}
<div>
<label for="title">タイトル</label>
<input type="text" id="title" name="title"
value="{{ old('title', $task->title) }}">
{{-- old('title', $task->title)
→ バリデーション失敗時は入力値を
→ 初回表示時は既存のデータを表示 --}}
@error('title') <p>{{ $message }}</p> @enderror
</div>
<div>
<label for="body">内容</label>
<textarea id="body" name="body">{{ old('body', $task->body) }}</textarea>
</div>
<div>
<label>
<input type="checkbox" name="done" value="1"
{{ old('done', $task->done) ? 'checked' : '' }}>
完了済みにする
</label>
</div>
<button type="submit">保存する</button>
<a href="{{ route('tasks.show', $task->id) }}">キャンセル</a>
</form>
@endsection
⑦ 削除(destroy)
コントローラー
// 📁 app/Http/Controllers/TaskController.php
// destroy() を書き換える
public function destroy(Task $task)
{
$task->delete();
return redirect()->route('tasks.index')
->with('success', 'タスクを削除しました');
}
削除ボタンは詳細ページのビュー(show.blade.php)にすでに追加済みです。
⑧ 動かしてみる
php artisan serve でサーバーを起動し、http://localhost:8000/tasks を開きます。
- 「+ タスクを追加」からタスクを登録する
- タスク名をクリックして詳細を見る
- 「編集」からタイトルや内容を変更する
- 「削除」でタスクを消す
これで基本的なCRUDアプリが完成です!
最終的なファイル構成
このシリーズで作ったファイルのまとめです。
myapp/
├── routes/
│ └── web.php ← Route::resource('tasks', ...) を追加
├── app/
│ ├── Http/Controllers/
│ │ └── TaskController.php ← 7つのメソッドを実装
│ └── Models/
│ └── Task.php ← $fillable を設定
├── database/
│ ├── migrations/
│ │ └── xxxx_create_tasks_table.php ← tasks テーブルの設計
│ └── seeders/
│ └── TaskSeeder.php ← テストデータ
└── resources/
└── views/
├── layouts/
│ └── app.blade.php ← 共通レイアウト
└── tasks/
├── index.blade.php ← 一覧ページ
├── create.blade.php ← 作成フォーム
├── show.blade.php ← 詳細ページ
└── edit.blade.php ← 編集フォーム
シリーズのまとめ
| 回 | 学んだこと | 主なファイル |
|---|---|---|
| #01 | LaravelとMVCの概念・環境構築 | routes/web.php |
| #02 | ルーティングとコントローラー | routes/web.php, Controllers/ |
| #03 | Bladeテンプレートとレイアウト | resources/views/ |
| #04 | マイグレーションとシーダー | database/migrations/, seeders/ |
| #05 | EloquentでDBを操作 | app/Models/ |
| #06 | フォームとバリデーション | フォームビュー, store() |
| #07 | CRUDアプリを完成させる | 全体統合 |
Laravelの基礎をひととおり体験できました。次のステップとして以下に挑戦してみてください。
- 認証:ログイン・登録機能(
composer require laravel/breeze) - ページネーション? データを複数ページに分けて表示する仕組み。100件のデータを1ページ10件ずつ表示するといった用途に使う。 :大量データをページ分けして表示(
->paginate(10)) - リレーション? テーブルとテーブルの関係のこと。「1人のユーザーは複数の投稿を持つ」のような関係をコードで表現できる。 :ユーザーとタスクをユーザーIDで紐づける
最初は難しく感じても、手を動かし続けるうちに必ずわかってきます。お疲れさまでした!