Laravel 13でBladeの新規登録フォームを作成する:バリデーション・CSRF・入力値の再表示
作成日:2026.08.21
Laravel 13のBladeで、既存のmembersテーブルへnameとemailを登録する新規登録フォームを作成します。$request->validate()による入力値の検証、@csrfによるCSRF対策、@errorによるエラー表示、old()による入力値の再表示、登録後のリダイレクトメッセージまでを確認します。
目次
前回は、Laravel 13でSQLiteを使い、membersテーブルとMember Modelを用意して、TinkerからCRUDを実行しました。
今回はその続きとして、Bladeで新規登録フォームを作成します。フォームへ入力した名前とメールアドレスをバリデーションし、問題がなければmembersテーブルへ保存します。入力に誤りがある場合は、エラーメッセージを表示し、入力済みの値をフォームへ戻します。
今回はLaravelのフォーム処理に絞って確認します。
前提
今回の対象環境は以下の通りです。
- OS: Windows 11
- PHP: 8.3以上
- Composer
- Laravel Framework: 13.x
- SQLite
- ターミナル: PowerShell
Laravel 13のプロジェクトを作成し、SQLiteの設定とmembersテーブルのMigration、Member Modelを用意している状態から始めます。プロジェクトの作成方法や、テーブルの準備については、次の記事を参照してください。
前回作成したmembersテーブルには、今回使用するnameとemailのカラムがあります。
members
├─ id
├─ name
├─ email
├─ created_at
└─ updated_at
このテーブルは説明用のものです。Laravelの認証用ユーザーを管理するテーブルではありません。
フォーム処理の流れ
今回は、フォームを表示する処理と、入力値を登録する処理を分けます。
GET /members/create
↓
MemberController@create
↓
resources/views/members/create.blade.php
POST /members
↓
MemberController@store
↓
バリデーション
↓
Member::create()
↓
登録完了メッセージを付けてフォームへリダイレクト
入力値に問題がなければ、検証済みの値だけをMember Modelへ渡します。バリデーションに失敗した場合は、Laravelが元のページへリダイレクトし、エラー情報と入力値を次のリクエストで利用できるようにします。
Member Modelの設定を確認する
今回は、Controllerから配列をMember::create()へ渡します。Modelに保存を許可する属性を指定しておく必要があるため、app/Models/Member.phpを確認します。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
#[Fillable(['name', 'email'])]
class Member extends Model
{
//
}
#[Fillable(['name', 'email'])]は、配列から保存できる属性をnameとemailに限定する設定です。リクエストの入力値をそのままModelへ渡すのではなく、後ほどバリデーション済みの値だけを渡します。
この設定は、前回の記事でMember::create()を使う場合の例として紹介したものです。すでに同じ設定を追加している場合は、重複して記述する必要はありません。
ルートを追加する
まず、routes/web.phpへフォーム表示用と登録処理用のルートを追加します。
<?php
use App\Http\Controllers\MemberController;
use Illuminate\Support\Facades\Route;
Route::get('/members/create', [MemberController::class, 'create'])
->name('members.create');
Route::post('/members', [MemberController::class, 'store'])
->name('members.store');
GETの/members/createはフォームを表示します。フォームを送信したときは、POSTの/membersへリクエストを送ります。
POST処理に別のURLを使うこともできますが、今回はLaravel公式のバリデーション例に近い構成にしています。ルート名を付けておくと、Bladeのroute()でURLを生成できるため、URLを直接書く箇所を減らせます。
Controllerを作成する
Controllerがまだない場合は、プロジェクトのルートディレクトリで次のコマンドを実行します。
php artisan make:controller MemberController
app/Http/Controllers/MemberController.phpを、次の内容にします。
<?php
namespace App\Http\Controllers;
use App\Models\Member;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class MemberController extends Controller
{
public function create(): View
{
return view('members.create');
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255'],
], [
'name.required' => '名前を入力してください。',
'name.string' => '名前は文字列で入力してください。',
'name.max' => '名前は255文字以内で入力してください。',
'email.required' => 'メールアドレスを入力してください。',
'email.email' => 'メールアドレスの形式で入力してください。',
'email.max' => 'メールアドレスは255文字以内で入力してください。',
]);
Member::create($validated);
return to_route('members.create')
->with('success', 'メンバーを登録しました。');
}
}
createメソッドでは、フォーム用のBladeを返します。storeメソッドでは、最初に入力値を検証し、検証に成功した場合だけMember::create()を実行します。
$request->validate()は、バリデーションに成功すると検証済みの値を返します。失敗した場合は、その後のMember::create()まで処理が進みません。
バリデーションルール
今回指定したルールは、次の通りです。
required: 値の入力を必須にするstring: 文字列であることを確認するemail: メールアドレスの形式を確認するmax:255: 255文字以内であることを確認する
今回はmembers.emailに、同じメールアドレスの重複チェックは行っていません。実際の会員登録機能で重複を禁止する場合は、データベースとバリデーションルールを別途設計します。
Bladeで新規登録フォームを作成する
resources/views/members/create.blade.phpを作成します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>メンバー登録</title>
</head>
<body>
<main>
<h1>メンバー登録</h1>
@if (session('success'))
<p>{{ session('success') }}</p>
@endif
<form method="POST" action="{{ route('members.store') }}">
@csrf
<div>
<label for="name">名前</label>
<input
id="name"
type="text"
name="name"
value="{{ old('name') }}"
>
@error('name')
<p>{{ $message }}</p>
@enderror
</div>
<div>
<label for="email">メールアドレス</label>
<input
id="email"
type="email"
name="email"
value="{{ old('email') }}"
>
@error('email')
<p>{{ $message }}</p>
@enderror
</div>
<button type="submit">登録</button>
</form>
</main>
</body>
</html>
フォームのmethodはPOST、送信先は登録処理用のmembers.storeルートです。LaravelのBladeでは、route()の結果やセッションのメッセージを、通常のBlade記法でHTMLへ出力できます。
@csrfを追加する
@csrfは、フォームへCSRFトークンを埋め込むBladeディレクティブです。HTMLへ変換されると、hidden属性の入力項目として出力されます。
Laravel 13のリクエスト偽造対策では、ブラウザが送信するOrigin情報を確認する処理と、CSRFトークンを確認する処理が使われます。接続条件やブラウザによってOriginの確認が利用できない場合は、従来のトークン検証へ進みます。そのため、POST、PUT、PATCH、DELETEのHTMLフォームには、明示的に@csrfを含めておくのが基本です。
@csrfを省略した場合の結果は、Laravelの設定やリクエスト条件によって異なります。トークン検証が行われる条件では、CSRFトークンの不一致としてリクエストが拒否されることがあります。CSRF対策を無効にして送信を通すのではなく、フォームへ@csrfを追加するようにします。
old()で入力値を再表示する
バリデーションに失敗すると、Laravelは入力値を次のリクエストで利用できるようにします。Bladeではold('name')やold('email')を使うと、直前に入力した値をvalue属性へ戻せます。
例えば、名前を入力した状態でメールアドレスの検証に失敗すると、フォームへ戻ったときに名前をもう一度入力する必要がありません。
@errorでエラーを表示する
@error('name')は、nameのバリデーションエラーが存在する場合に、ブロック内を表示します。エラーメッセージは、ブロック内で利用できる$messageに入っています。
今回はController側で日本語のメッセージを指定しているため、画面には「名前を入力してください。」や「メールアドレスの形式で入力してください。」などが表示されます。
フォームを表示する
Laravelの開発サーバーを起動します。
php artisan serve
ブラウザで次のURLを開きます。
http://127.0.0.1:8000/members/create
名前とメールアドレスの入力欄、「登録」ボタンが表示されれば、GETルート、Controller、Bladeの接続を確認できています。
バリデーションの動作を確認する
必須項目を空にして送信する
何も入力せずに「登録」をクリックします。
バリデーションに失敗すると、ControllerのMember::create()は実行されず、元のフォーム画面へ戻ります。画面には、次のエラーメッセージが表示されます。
- 名前を入力してください。
- メールアドレスを入力してください。
メールアドレスの形式を確認する
名前に「テストユーザー」、メールアドレスに「invalid」などを入力して送信します。
メールアドレスの形式が正しくないため、メールアドレス欄に「メールアドレスの形式で入力してください。」と表示されます。名前の入力値はold('name')によって再表示されます。
この時点では、バリデーションに失敗しているため、membersテーブルへレコードは追加されません。
正しい値を入力する
次のような値を入力します。
- 名前: テストユーザー
- メールアドレス:
user@example.test
「登録」をクリックしてバリデーションに成功すると、Member::create($validated)が実行され、membersテーブルへレコードが追加されます。
登録後は、members.createへリダイレクトし、画面に「メンバーを登録しました。」と表示します。今回のフォームには一覧画面を用意していないため、登録結果は次のTinkerで確認します。
php artisan tinker
App\Models\Member::orderByDesc('id')
->first(['id', 'name', 'email']);
直前に登録したnameとemailが表示されれば、フォームからデータベースまでの処理を確認できています。確認が終わったら、Tinkerでexitを入力します。
うまく動かない場合
419やCSRFに関するエラーになる
まず、Bladeのフォーム内に@csrfがあるか確認します。フォームのmethodがPOSTになっているか、送信先がmembers.storeルートになっているかも確認してください。
セッションを利用できない状態や、Cookieが無効になっている状態でも、CSRFトークンの検証に失敗することがあります。ブラウザのCookie設定、.envのセッション設定、Laravelのログも確認します。
MassAssignmentExceptionになる
Member::create($validated)でMass Assignmentに関する例外が発生する場合は、Member Modelに#[Fillable(['name', 'email'])]が設定されているか確認します。
また、$validatedに含まれるキーと、Fillableで許可したキーが一致しているかも確認します。今回のフォームでは、保存対象をnameとemailに限定しています。
View [members.create] not foundになる
Controllerではview('members.create')を指定しているため、Bladeファイルは次の場所に必要です。
resources/views/members/create.blade.php
ディレクトリ名、ファイル名、.blade.phpの拡張子が一致しているか確認します。
could not find driverになる
SQLiteへ接続するときにcould not find driverと表示される場合は、PHPのSQLite用PDO拡張が有効になっていない可能性があります。
php --ini
php -m | Select-String -Pattern 'PDO|sqlite'
pdo_sqliteとsqlite3が表示されるか確認します。PHPを複数インストールしている場合は、PowerShellで実行しているPHPと、Laravelが想定しているPHPが一致しているかも確認してください。
注意点
- 今回の
membersテーブルは、フォーム処理を確認するための説明用テーブルであり、Laravelの認証用ユーザー管理ではない。 - メールアドレスの形式を検証しても、実在性やメールの到達性まで確認できるわけではない。
- フォームから受け取った値は、バリデーション済みの値だけをModelへ渡す。
- Bladeで入力値やメッセージを出力するときは、基本的に
{{ }}を使う。{!! !!}で未検証の値をHTMLとして出力しない。 - CSRF対策を無効にしてフォーム送信を通すのではなく、対象フォームへ
@csrfを追加する。 - 実際の会員登録機能では、認証、認可、重複メールアドレス、個人情報保護、レート制限などを別途設計する。
まとめ
今回は、前回作成したmembersテーブルとMember Modelを使い、Bladeで新規登録フォームを作成しました。
フォームには@csrfを追加し、Controllerでは$request->validate()でnameとemailを検証しました。検証に成功した値だけをMember::create()へ渡すことで、フォームからSQLiteへデータを登録できます。
バリデーションに失敗したときは、@errorでエラーを表示し、old()で入力済みの値を再表示しました。Laravelのフォーム処理では、入力画面、Controllerの検証と保存、エラー時の戻し方を分けて考えると、処理の流れを整理しやすいかと思います。
詳しい仕様は、Laravel公式のValidation、CSRF Protection、Eloquentも参照してください。
奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。
これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。
【制作会社・企業様向けサポート】
Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。
ぜひ、プロフィールやWeb制作会社様向け業務案内、一般企業様向け業務案内もご覧くださいね。
Laravel Pintの使い方:PHPコードの書式を整えてCIで検査する
2026.09.15
Laravel Pintを使ってPHPコードの書式を整える方法を紹介します。ローカルでの自動修正、CIで書式違反だけを検査する--test、対象範囲の指定、pint.jsonの設定、Bladeファイルを扱う際の注意点を整理し、Pintを導入する判断基準も説明します。
GitHub ActionsでPHP・LaravelのCIを始める:テスト・Lint・ビルドを自動化するWorkflowの基本
2026.09.13
PHP・Laravelプロジェクトを対象に、GitHub ActionsのWorkflow、イベント、ジョブ、ステップの基本を説明します。Composerのテスト、npm ci、フロントエンドビルド、Laravel PintをCIへ組み込む方法に加え、featureブランチやPull Requestで実行される条件、失敗時のログ確認、Secrets・権限・Actionのバージョン管理についても整理します。
Laravel 13でArtisanコマンドを作成する:引数・オプション・dry-runの基本
2026.09.10
Laravel 13でArtisanコマンドを作成する方法を、引数・オプション・コンソール出力・終了コードの基本から解説します。日時指定した記事を定期的に公開するDB更新処理を例に、--dry-runで対象を確認してから安全に実行する方法や、スケジューラー・cronでの定期実行についても紹介します。
LaravelのFeature Testで開発用DBを初期化してしまった:Codex運用で学ぶテストDBの多層防御
2026.09.07
CodexにLaravelのFeature Test実行を依頼した際、テスト用DBではなくローカルの開発用DBへ接続した状態でRefreshDatabaseが実行され、データが失われる事故が発生しました。この記事では、PHPUnitのforce="true"による環境変数固定、DB_URLの無効化、config:clearのComposer Script化、Laravel起動後のDB接続先ガード、通常テストとIntegration Testの分離など、テストDBの誤接続を防ぐ多層防御を紹介します。