#06 Laravelを始めよう

フォームとバリデーション

CRUD? データ操作の基本4種「Create(作成)・Read(取得)・Update(更新)・Delete(削除)」の頭文字。Webアプリの基本機能。 の「C(Create)」「U(Update)」にはフォームが欠かせません。今回はHTMLフォームでデータを受け取り、 バリデーション? フォームに入力されたデータが正しい形式かをチェックする処理。「メールアドレスの形式か」「空欄でないか」などを検証する。 で検証する流れを一通り実装します。


新しいルートを追加する

フォームには2種類のルートが必要です。「フォームページを表示する」と「送信されたデータを受け取って処理する」の2つです。

routes/web.php を開き、次の2行を追加してください(use App\Http\Controllers\TaskController; の行もなければ追加します)。

<?php
// 📁 routes/web.php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TaskController; // ← なければ追加

// 既存のルートの下に追加
Route::get('/tasks/create', [TaskController::class, 'create']); // フォームページ表示
Route::post('/tasks', [TaskController::class, 'store']);         // データ受け取り

get はページを表示するだけ、post はフォームからデータが送られてきたときの処理です。


コントローラーにメソッドを追加する

app/Http/Controllers/TaskController.php を開き、2つのメソッドを追加します。

<?php
// 📁 app/Http/Controllers/TaskController.php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request; // ← この行が先頭にあることを確認

class TaskController extends Controller
{
    // ──────────────────────────────────────────────
    // フォームページを表示する
    // ──────────────────────────────────────────────
    public function create()
    {
        return view('tasks.create');
        // resources/views/tasks/create.blade.php を表示
    }

    // ──────────────────────────────────────────────
    // フォームの送信を受け取って保存する
    // ──────────────────────────────────────────────
    public function store(Request $request)
    {
        // $request にフォームのデータが入っている
        $title = $request->input('title'); // フォームの name="title" の値
        $body  = $request->input('body');  // フォームの name="body" の値

        Task::create([
            'title' => $title,
            'body'  => $body,
            'done'  => false,
        ]);

        return redirect('/tasks'); // 保存後に一覧ページへ移動
    }
}

Request $request は送られてきたリクエスト全体を受け取るオブジェクトです。$request->input('フィールドのname属性') でフォームの値を取り出せます。


フォームのビューを作る

resources/views/tasks/create.blade.php を新規作成します。

myapp/
└── resources/
    └── views/
        └── tasks/          ← フォルダを新規作成(なければ)
            └── create.blade.php ← ファイルを新規作成

resources/views/tasks/create.blade.php の内容:

<!-- 📁 resources/views/tasks/create.blade.php(新規作成) -->
@extends('layouts.app')

@section('title', 'タスクを追加')

@section('content')
<h1>タスクを追加</h1>

<form method="POST" action="/tasks">
    @csrf

    <div>
        <label for="title">タイトル</label>
        <input type="text" id="title" name="title" value="{{ old('title') }}">
    </div>

    <div>
        <label for="body">内容</label>
        <textarea id="body" name="body">{{ old('body') }}</textarea>
    </div>

    <button type="submit">追加する</button>
</form>
@endsection

@csrf は必ず書く

@csrf はフォームの中に必ず入れてください。これは CSRF? 悪意あるサイトから偽のリクエストを送りつける攻撃手法。Laravelはフォームに隠しトークンを埋め込んで防いでいる。 攻撃を防ぐための隠しトークンを埋め込む命令です。書き忘れると、フォームを送信したときに「419 Page Expired」というエラーが表示されます。

実際に展開されると次のような隠しフィールドになっています。

<input type="hidden" name="_token" value="ランダムな文字列">

バリデーションを追加する

フォームから送られたデータをそのまま保存するのは危険です。 バリデーション? フォームに入力されたデータが正しい形式かをチェックする処理。「メールアドレスの形式か」「空欄でないか」などを検証する。 でデータが正しい形式かを確認してから保存します。

TaskController.phpstore() メソッドを次のように書き換えます。

// 📁 app/Http/Controllers/TaskController.php
// store() メソッドを書き換える

public function store(Request $request)
{
    // バリデーション(検証)を実行する
    $validated = $request->validate([
        'title' => ['required', 'max:100'],    // 必須・100文字以内
        'body'  => ['nullable', 'max:1000'],   // 任意・1000文字以内
    ]);
    // → 検証に失敗すると自動でフォームページに戻る
    // → 成功すると $validated に検証済みの値が入る

    Task::create([
        'title' => $validated['title'],
        'body'  => $validated['body'] ?? null,
        'done'  => false,
    ]);

    return redirect('/tasks');
}

validate() の配列には「'フィールド名' => ['ルール1', 'ルール2', ...]」を書きます。

よく使うバリデーションルール:

ルール意味
required必須(空欄は不可)
nullable空欄でもOK
max:100最大100文字
min:8最小8文字
emailメールアドレスの形式
integer整数
unique:usersusersテーブルで重複しない

バリデーションエラーを表示する

validate() に失敗すると、Laravelは自動でフォームページに戻り $errors という変数を渡してくれます。ビューにエラー表示を追加しましょう。

resources/views/tasks/create.blade.php<form> タグの直前に追加します。

<!-- 📁 resources/views/tasks/create.blade.php -->
<!-- <form> タグの直前に追加 -->

@if ($errors->any())
    <div style="color: red;">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

特定のフィールドのエラーだけ、フィールドの直下に表示するには @error ディレクティブが使えます。

<!-- 📁 resources/views/tasks/create.blade.php -->
<!-- title の input タグの直後に追加 -->

<input type="text" id="title" name="title" value="{{ old('title') }}">
@error('title')
    <p style="color: red;">{{ $message }}</p>
@enderror

old() で入力値を保持する

old('title') は「直前にこのフィールドに入力された値」を返します。バリデーションに失敗してフォームページに戻ったとき、入力した値が消えずに残るようになります。


完了メッセージを表示する

タスクを保存した後「追加しました」と表示するには フラッシュメッセージ? リダイレクト後に一度だけ表示されるメッセージ。「保存しました」などの完了通知によく使われる。次のリクエスト後は消える。 を使います。

store() メソッドの redirect() 部分を次のように変更します。

// 📁 app/Http/Controllers/TaskController.php
// store() の最後の return を書き換える

return redirect('/tasks')->with('success', 'タスクを追加しました');
//                        ↑ セッションに一時的にメッセージを保存

次に、一覧ページのビュー resources/views/tasks/index.blade.php を開き、一覧の上にメッセージ表示を追加します。

<!-- 📁 resources/views/tasks/index.blade.php -->
<!-- <h1> タグの直後に追加 -->

@if (session('success'))
    <p>{{ session('success') }}</p>
@endif

このメッセージはリダイレクト後に1回だけ表示されます。ページをリロードすると自動的に消えます。


リダイレクトとは

リダイレクト? ユーザーを別のURLへ誘導すること。フォーム送信後に一覧ページへ飛ばすなど、操作の完了を示すときによく使う。 は「今のページからあのURLへ移動して」とブラウザに指示することです。

フォーム送信後にそのまま同じページに留まると、ブラウザをリロードしたときにフォームが再送信されてしまいます(データが2重登録される)。これを防ぐためにも、保存後は別ページへリダイレクトする習慣をつけましょう。


まとめ

この回でやったこと:

  • routes/web.php にGETとPOSTの2つのルートを追加した
  • create() でフォームビューを返し、store() で入力を受け取った
  • $request->input() でフォームの値を取り出した
  • $request->validate() でバリデーションを実装した
  • $errorsold() でエラー時の表示を整えた
  • flash_message で完了メッセージを表示した

次回はいよいよ最終回。ここまでの知識をすべて使ってタスク管理アプリのCRUDを完成させます