【保存版】Laravel Formの完全ガイド:実装手順と9つのベストプラクティス

Laravel Formとは:基礎から理解する重要性

Webアプリケーション開発において、フォーム処理は最も重要な機能の一つです。Laravel Formは、このフォーム処理を効率的かつ安全に実装するためのツールセットを提供します。本記事では、Laravel Formの基礎から実践的な実装テクニック、そしてセキュリティ対策まで、包括的に解説していきます。

フレームワークにおけるフォーム処理の位置づけ

フォーム処理は、以下の観点からWebアプリケーションの中核を担う重要な要素となっています:

  1. データの入り口としての役割
  • ユーザーからの入力を受け付ける最前線
  • アプリケーションのデータフローの起点
  • ビジネスロジックの実行トリガー
  1. セキュリティの要所
  • XSS攻撃やCSRF攻撃の主要なターゲット
  • 入力値の検証と無害化が必須
  • セッション管理との密接な連携
  1. ユーザー体験の決定要因
  • フォームの使いやすさがUXに直結
  • エラーハンドリングの適切な実装が重要
  • レスポンス速度への影響が大きい

Laravel Formが解決する3つの課題

Laravel Formは、従来のフォーム実装で直面する以下の課題を効果的に解決します:

1. 開発効率の向上

// 従来の方法
<form method="POST" action="/profile">
    <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
    <!-- 多数のフォーム要素を手動で記述 -->
</form>

// Laravel Form使用時
{!! Form::open(['url' => 'profile']) !!}
    // 自動的にCSRFトークンが追加される
    // Form Builderによる効率的な実装
{!! Form::close() !!}

2. セキュリティ対策の標準化

  • CSRFトークンの自動生成と検証
  • 入力値のエスケープ処理の自動化
  • バリデーションルールの一元管理

3. 保守性の向上

機能メリット
Form Requestバリデーションロジックの分離
Validation Rulesルールの再利用性向上
Error Handlingエラー処理の一貫性確保

Laravel Formを使用することで、開発者は以下のメリットを得ることができます:

  • 生産性の向上: ボイラープレートコードの削減
  • 品質の確保: セキュリティ対策の標準化
  • メンテナンス性: コードの一貫性と可読性の向上

次のセクションでは、これらの機能を実際に実装する手順について、具体的なコード例を交えながら解説していきます。

Laravel Formの基本実装手順

フォーム機能を実装する際の基本的な手順を、実践的なコード例とともに解説します。初めてLaravel Formを使用する方でも理解しやすいよう、ステップバイステップで説明していきます。

フォームの基本構造を作成する

まず、Laravel Collectiveパッケージをインストールします:

composer require laravelcollective/html

設定ファイルに以下のプロバイダーを追加:

// config/app.php
'providers' => [
    // ...
    Collective\Html\HtmlServiceProvider::class,
],

'aliases' => [
    // ...
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
]

基本的なフォーム構造の実装例:

// resources/views/form.blade.php
{!! Form::open(['url' => 'posts', 'method' => 'POST', 'files' => true]) !!}
    <div class="form-group">
        {!! Form::label('title', 'タイトル') !!}
        {!! Form::text('title', null, ['class' => 'form-control']) !!}
    </div>

    <div class="form-group">
        {!! Form::label('content', '内容') !!}
        {!! Form::textarea('content', null, ['class' => 'form-control']) !!}
    </div>

    {!! Form::submit('投稿', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}

CSRFトークンを適切に設定する

LaravelのCSRF保護は以下の手順で実装します:

  1. ミドルウェアの確認
// app/Http/Kernel.php
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\VerifyCsrfToken::class,
        // ...
    ],
];
  1. トークンの除外設定(必要な場合)
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
    'payment/*', // 外部決済システムのコールバックなど
    'api/*',     // API エンドポイント
];
  1. JavaScriptでのトークン取得
// AJAXリクエスト時のトークン設定
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

バリデーションルールを実装する

フォームのバリデーションは、Form Requestクラスを使用して実装することをお勧めします:

// app/Http/Requests/PostRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class PostRequest extends FormRequest
{
    public function authorize()
    {
        return true; // 認証チェック
    }

    public function rules()
    {
        return [
            'title' => 'required|max:255',
            'content' => 'required|min:10',
            'image' => 'image|max:2048', // 2MB制限
        ];
    }

    public function messages()
    {
        return [
            'title.required' => 'タイトルは必須です',
            'content.required' => '内容は必須です',
            'content.min' => '内容は10文字以上入力してください',
        ];
    }
}

コントローラでの使用例:

// app/Http/Controllers/PostController.php
public function store(PostRequest $request)
{
    // バリデーション済みのデータを取得
    $validated = $request->validated();

    // データベースへの保存処理
    Post::create($validated);

    return redirect()->route('posts.index')
        ->with('success', '投稿が完了しました');
}

実装時の重要なポイント:

項目推奨事項
フォーム方式マルチパートフォームの使用(ファイルアップロード対応)
エラー表示Bladeディレクティブでの一括表示
バリデーションForm Requestクラスでの一元管理
リダイレクトwithInput()メソッドでの入力値保持

以上の基本実装を土台として、次のセクションでは、より実践的なフォーム実装テクニックについて解説していきます。

実践的なフォーム実装テクニック

基本的なフォーム実装を理解したところで、より実践的なテクニックについて解説します。このセクションでは、実務でよく必要となる高度な実装方法を紹介します。

ファイルアップロード機能の実装方法

ファイルアップロードを安全かつ効率的に実装する方法を説明します:

// config/filesystems.php での設定
'disks' => [
    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],
],

フォームの実装例:

// resources/views/upload-form.blade.php
{!! Form::open(['route' => 'files.store', 'files' => true, 'class' => 'dropzone']) !!}
    <div class="form-group">
        {!! Form::file('file', [
            'class' => 'form-control',
            'accept' => '.pdf,.doc,.docx,image/*'
        ]) !!}
    </div>

    // プログレスバーの実装
    <div class="progress">
        <div class="progress-bar" role="progressbar"></div>
    </div>
{!! Form::close() !!}

ファイル処理のコントローラ実装:

public function store(Request $request)
{
    $request->validate([
        'file' => 'required|file|max:10240|mimes:pdf,doc,docx,jpg,png'
    ]);

    try {
        // ファイルの保存と最適化
        $path = $request->file('file')->store('uploads', 'public');

        // 画像の場合はリサイズ処理
        if ($request->file('file')->isImage()) {
            $image = Image::make(storage_path("app/public/{$path}"));
            $image->resize(800, null, function ($constraint) {
                $constraint->aspectRatio();
                $constraint->upsize();
            });
            $image->save();
        }

        return response()->json([
            'success' => true,
            'path' => $path
        ]);
    } catch (\Exception $e) {
        return response()->json([
            'success' => false,
            'message' => '処理中にエラーが発生しました'
        ], 500);
    }
}

非同期通信(Ajax)との連携手法

モダンなユーザー体験を提供するためのAjax実装:

// resources/js/form-handler.js
const submitForm = async (formData) => {
    try {
        const response = await axios.post('/api/submit', formData, {
            headers: {
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
            },
            onUploadProgress: (progressEvent) => {
                const percentCompleted = Math.round(
                    (progressEvent.loaded * 100) / progressEvent.total
                );
                updateProgressBar(percentCompleted);
            }
        });

        handleSuccess(response.data);
    } catch (error) {
        handleError(error.response.data);
    }
};

Laravelでの非同期リクエスト処理:

// app/Http/Controllers/ApiController.php
public function submit(Request $request)
{
    $validated = $request->validate([
        'data' => 'required|array',
        'files.*' => 'sometimes|file|max:5120'
    ]);

    return response()->json([
        'message' => '正常に処理されました',
        'data' => $processedData
    ]);
}

多言語対応の実現方法

国際化(i18n)対応のフォーム実装:

// resources/lang/ja/validation.php
return [
    'custom' => [
        'email' => [
            'required' => 'メールアドレスは必須です',
            'email' => '有効なメールアドレスを入力してください',
        ],
    ],
];

// resources/lang/en/validation.php
return [
    'custom' => [
        'email' => [
            'required' => 'The email field is required',
            'email' => 'Please enter a valid email address',
        ],
    ],
];

多言語フォームの実装例:

// resources/views/multilingual-form.blade.php
{!! Form::open(['route' => 'contact.store', 'method' => 'POST']) !!}
    <div class="form-group">
        {!! Form::label('name', __('forms.name')) !!}
        {!! Form::text('name', null, [
            'class' => 'form-control',
            'placeholder' => __('forms.name_placeholder')
        ]) !!}
    </div>

    @foreach(config('app.available_locales') as $locale)
        <div class="form-group">
            {!! Form::label("title_{$locale}", __('forms.title')." ({$locale})") !!}
            {!! Form::text("title_{$locale}", null, ['class' => 'form-control']) !!}
        </div>
    @endforeach
{!! Form::close() !!}

実装のベストプラクティス:

機能実装のポイント
ファイルアップロード– チャンク分割アップロード
– プログレス表示
– バリデーション
Ajax通信– エラーハンドリング
– ローディング表示
– 応答待ち制御
多言語対応– 翻訳ファイルの管理
– フォールバック設定
– 動的な言語切り替え

これらの実装テクニックを適切に組み合わせることで、より使いやすく保守性の高いフォーム機能を実現できます。次のセクションでは、セキュリティ対策について詳しく解説していきます。

セキュリティ対策の実装ガイド

フォーム処理におけるセキュリティ対策は、Webアプリケーションの信頼性を確保する上で最も重要な要素の一つです。このセクションでは、主要な脆弱性への具体的な対策方法を解説します。

XSS攻撃からの防御方法

クロスサイトスクリプティング(XSS)攻撃から守るための実装例:

// config/session.php での設定
'cookie_httponly' => true,
'cookie_secure' => env('SESSION_SECURE_COOKIE', true),
'same_site' => 'lax',

// Bladeテンプレートでの安全な出力
{{-- 危険な例 --}}
{!! $userInput !!}  // エスケープなしの出力

{{-- 安全な例 --}}
{{ $userInput }}    // 自動エスケープされる

入力値のサニタイズ処理:

// app/Http/Middleware/SanitizeInput.php
namespace App\Http\Middleware;

class SanitizeInput
{
    public function handle($request, \Closure $next)
    {
        $input = $request->all();

        array_walk_recursive($input, function(&$value) {
            // HTMLタグの除去
            $value = strip_tags($value);
            // 特殊文字のエスケープ
            $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
        });

        $request->merge($input);
        return $next($request);
    }
}

CSRFトークンの詳細設定

より堅牢なCSRF保護の実装方法:

// app/Http/Middleware/VerifyCsrfToken.php
namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        // CSRFチェックを除外するURL
        'stripe/*',
        'webhook/*'
    ];

    /**
     * CSRF トークンとセッションを検証する
     */
    protected function tokensMatch($request)
    {
        $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

        if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
            $token = $this->encrypter->decrypt($header, static::serialized());
        }

        return is_string($token) && 
               is_string($request->session()->token()) &&
               hash_equals($request->session()->token(), $token);
    }
}

Ajaxリクエストでの実装:

// CSRFトークンの自動設定
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content;

// トークンの定期的な更新
setInterval(() => {
    axios.get('/csrf-token/refresh')
        .then(response => {
            document.querySelector('meta[name="csrf-token"]').content = response.data.token;
        });
}, 3600000); // 1時間ごと

入力値の無害化処理の実装

安全な入力値処理の実装例:

// app/Rules/SafeHtml.php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use HTMLPurifier;
use HTMLPurifier_Config;

class SafeHtml implements Rule
{
    private $purifier;

    public function __construct()
    {
        $config = HTMLPurifier_Config::createDefault();
        $config->set('HTML.Allowed', 'p,b,i,u,a[href],ul,ol,li');
        $this->purifier = new HTMLPurifier($config);
    }

    public function passes($attribute, $value)
    {
        $cleaned = $this->purifier->purify($value);
        return $cleaned === $value;
    }

    public function message()
    {
        return '安全でないHTMLが含まれています。';
    }
}

セキュリティ対策のチェックリスト:

対策項目実装内容
入力検証– データ型の確認
– 長さ制限
– フォーマット検証
出力エスケープ– HTMLエスケープ
– JavaScriptエスケープ
– URLエンコード
セッション保護– セッションID再生成
– Cookieセキュリティ設定
– セッションタイムアウト

セキュリティ実装のベストプラクティス:

  1. データ検証の多層化
  • フロントエンド(JavaScript)
  • バックエンド(PHP)
  • データベース(制約)
  1. エラーメッセージの適切な制御
   // 本番環境での詳細なエラー情報の制限
   config(['app.debug' => false]);

   // カスタムエラーハンドリング
   public function render($request, Exception $exception)
   {
       if ($exception instanceof ValidationException) {
           return response()->json([
               'message' => '入力内容を確認してください',
               'errors' => $exception->errors()
           ], 422);
       }

       return parent::render($request, $exception);
   }
  1. ファイルアップロードのセキュリティ
   // ファイルバリデーションの実装
   $request->validate([
       'file' => [
           'required',
           'file',
           'mimes:pdf,doc,docx',
           'max:10240',
           new VirusScan(),  // カスタムバリデーションルール
       ]
   ]);

これらのセキュリティ対策を適切に実装することで、安全なフォーム処理を実現できます。次のセクションでは、Laravelフォームのベストプラクティスについて解説していきます。

Laravel Formのベストプラクティス9選

効率的で保守性の高いフォーム実装を実現するための、9つのベストプラクティスを紹介します。これらの手法は実務での経験に基づいており、チーム開発での生産性向上にも貢献します。

フォームリクエストの効果的な活用法

  1. リクエストクラスの階層化
// app/Http/Requests/BaseFormRequest.php
abstract class BaseFormRequest extends FormRequest
{
    protected function failedValidation(Validator $validator)
    {
        throw new ValidationException($validator, response()->json([
            'message' => '入力内容に誤りがあります',
            'errors' => $validator->errors()
        ], 422));
    }

    protected function formatErrorMessages(array $errors): array
    {
        return array_map(function($error) {
            return [
                'message' => $error[0],
                'code' => 'VALIDATION_ERROR'
            ];
        }, $errors);
    }
}

// app/Http/Requests/UserRegistrationRequest.php
class UserRegistrationRequest extends BaseFormRequest
{
    public function rules(): array
    {
        return [
            'email' => ['required', 'email', 'unique:users'],
            'password' => ['required', 'min:8', 'confirmed'],
            'terms' => ['required', 'accepted']
        ];
    }

    public function messages(): array
    {
        return [
            'email.unique' => 'このメールアドレスは既に登録されています',
            'password.min' => 'パスワードは8文字以上で入力してください'
        ];
    }
}
  1. 条件付きバリデーションの実装
public function rules(): array
{
    $rules = [
        'name' => 'required|string|max:255',
        'type' => 'required|in:individual,company'
    ];

    if ($this->input('type') === 'company') {
        $rules['company_name'] = 'required|string|max:255';
        $rules['registration_number'] = 'required|string|size:13';
    }

    return $rules;
}

バリデーションの共通化と再利用

  1. カスタムバリデーションルールの作成
// app/Rules/StrongPassword.php
class StrongPassword implements Rule
{
    public function passes($attribute, $value): bool
    {
        // 少なくとも1つの大文字、小文字、数字、特殊文字を含む
        return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/', $value);
    }

    public function message(): string
    {
        return 'パスワードは大文字、小文字、数字、特殊文字を含む必要があります';
    }
}
  1. バリデーションルールの共通定義
// app/Rules/ValidationRules.php
class ValidationRules
{
    public static function phoneNumber(): array
    {
        return ['required', 'regex:/^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$/'];
    }

    public static function postalCode(): array
    {
        return ['required', 'regex:/^\d{3}-\d{4}$/'];
    }
}

// 使用例
public function rules(): array
{
    return [
        'phone' => ValidationRules::phoneNumber(),
        'postal_code' => ValidationRules::postalCode()
    ];
}

エラーメッセージのカスタマイズ戦略

  1. ユーザーフレンドリーなエラーメッセージ
// resources/lang/ja/validation.php
return [
    'custom' => [
        'email' => [
            'required' => 'メールアドレスを入力してください',
            'email' => '有効なメールアドレスを入力してください',
            'unique' => 'このメールアドレスは既に使用されています'
        ],
        'password' => [
            'min' => ':min文字以上のパスワードを設定してください',
            'confirmed' => 'パスワードと確認用パスワードが一致しません'
        ]
    ]
];
  1. 動的なエラーメッセージ生成
public function messages(): array
{
    $maxFileSize = config('upload.max_size');

    return [
        'file.max' => "ファイルサイズは{$maxFileSize}MB以下にしてください",
        'items.*.name.required' => ':index番目の項目名を入力してください'
    ];
}

protected function formatErrorMessage($message, $attribute, $rule, $parameters)
{
    return str_replace(
        [':index'],
        [intval(explode('.', $attribute)[1]) + 1],
        $message
    );
}
  1. エラー表示のコンポーネント化
// resources/views/components/form-error.blade.php
@props(['name'])

@error($name)
    <div class="error-message">
        <svg class="error-icon" viewBox="0 0 20 20">
            <path d="M10 18a8 8 0 100-16 8 8 0 000 16zM9 9V5h2v4h-2zm0 2h2v2H9v-2z"/>
        </svg>
        <span>{{ $message }}</span>
    </div>
@enderror

// 使用例
<x-form-error name="email" />

実装のポイント一覧:

ベストプラクティスメリット
リクエストクラスの階層化コードの再利用性向上、一貫性の確保
条件付きバリデーション柔軟な入力検証、UX向上
カスタムルール作成ビジネスロジックの分離、再利用性
エラーメッセージの一元管理保守性向上、多言語対応の容易さ
コンポーネント化UI一貫性、開発効率向上

これらのベストプラクティスを適切に組み合わせることで、保守性が高く、ユーザーフレンドリーなフォーム実装が実現できます。次のセクションでは、パフォーマンス最適化とデバッグ手法について解説していきます。

パフォーマンス最適化とデバッグ手法

フォーム処理のパフォーマンスとデバッグは、アプリケーションの品質を大きく左右します。このセクションでは、実践的な最適化手法とデバッグのテクニックを解説します。

フォーム処理の速度改善テクニック

  1. バリデーション処理の最適化
// app/Http/Requests/OptimizedFormRequest.php
class OptimizedFormRequest extends FormRequest
{
    // バリデーションルールのキャッシュ
    private static $cachedRules = [];

    public function rules()
    {
        $cacheKey = static::class . '_rules';

        if (!isset(self::$cachedRules[$cacheKey])) {
            self::$cachedRules[$cacheKey] = $this->generateRules();
        }

        return self::$cachedRules[$cacheKey];
    }

    // 必要なバリデーションのみを実行
    protected function failedValidation(Validator $validator)
    {
        $errors = $validator->errors()->all();
        throw new ValidationException($validator, response()->json([
            'message' => current($errors)
        ], 422));
    }
}
  1. ファイルアップロードの最適化
// config/filesystems.php
'disks' => [
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
        'cache' => [
            'store' => 'redis',
            'expire' => 600,
            'prefix' => 'file_cache:'
        ]
    ]
];

// app/Services/FileUploadService.php
class FileUploadService
{
    public function handleUpload(UploadedFile $file)
    {
        // チャンクアップロードの実装
        $chunk = $file->getStream()->read(1024 * 1024); // 1MB chunks
        $path = $this->generateUniquePath($file);

        while (!$file->getStream()->eof()) {
            Storage::disk('local')->append($path, $chunk);
            $chunk = $file->getStream()->read(1024 * 1024);
        }

        return $path;
    }

    // 画像最適化処理
    public function optimizeImage($path)
    {
        $image = Image::make(storage_path("app/{$path}"));

        // WebP形式への変換
        if ($image->mime() !== 'image/webp') {
            $webpPath = preg_replace('/\.[^.]+$/', '.webp', $path);
            $image->save(storage_path("app/{$webpPath}"), 80, 'webp');
            return $webpPath;
        }

        return $path;
    }
}

効率的なデバッグ方法の解説

  1. デバッグツールの活用
// app/Providers/AppServiceProvider.php
public function boot()
{
    if (config('app.debug')) {
        DB::listen(function($query) {
            Log::info(
                $query->sql,
                [
                    'bindings' => $query->bindings,
                    'time' => $query->time
                ]
            );
        });
    }
}

// デバッグ用のミドルウェア
class DebugFormSubmission
{
    public function handle($request, Closure $next)
    {
        if (config('app.debug')) {
            Log::debug('Form Submission', [
                'url' => $request->fullUrl(),
                'method' => $request->method(),
                'inputs' => $request->except(['password', 'password_confirmation']),
                'files' => $request->allFiles()
            ]);
        }

        return $next($request);
    }
}
  1. エラートレース機能の実装
// app/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
    if ($exception instanceof ValidationException) {
        return $this->handleValidationException($exception);
    }

    if (config('app.debug')) {
        return $this->renderDetailedError($exception);
    }

    return parent::render($request, $exception);
}

protected function renderDetailedError($exception)
{
    return response()->json([
        'message' => $exception->getMessage(),
        'file' => $exception->getFile(),
        'line' => $exception->getLine(),
        'trace' => collect($exception->getTrace())
            ->map(function ($trace) {
                return Arr::except($trace, ['args']);
            })
            ->all()
    ], 500);
}

実装時の注意点とトラブルシューティング

一般的な問題と解決策:

問題解決策
メモリ使用量の増大– ファイルのストリーム処理
– クエリの最適化
– 不要なデータの解放
レスポンス時間の遅延– キャッシュの活用
– 非同期処理の導入
– N+1問題の解消
バリデーションの重複– ルールの共通化
– キャッシュの活用
– 条件付きバリデーション

パフォーマンス改善のチェックリスト:

  1. データベースの最適化
// Eagerローディングの活用
$users = User::with(['profile', 'posts'])->get();

// クエリのキャッシュ
$users = Cache::remember('users', 3600, function () {
    return User::with(['profile'])->get();
});
  1. メモリ使用量の最適化
// コレクションの代わりにジェネレータを使用
function getUsers()
{
    $users = User::cursor();
    foreach ($users as $user) {
        yield $user;
    }
}

// 使用例
foreach (getUsers() as $user) {
    // メモリ効率の良い処理
}
  1. キャッシュ戦略
// フォームの初期データのキャッシュ
public function create()
{
    $formData = Cache::remember('form_initial_data', 3600, function () {
        return [
            'categories' => Category::all(),
            'tags' => Tag::all(),
            'settings' => Setting::getDefaults()
        ];
    });

    return view('form.create', compact('formData'));
}

これらの最適化とデバッグ手法を適切に実装することで、高性能で安定したフォーム処理を実現できます。次のセクションでは、実践的なユースケースについて解説していきます。

実践的なユースケース解説

実務でよく遭遇する具体的なユースケースについて、実装例とともに解説します。それぞれのケースで、これまでに説明したベストプラクティスやセキュリティ対策を適用した実装方法を紹介します。

会員登録フォームの実装例

  1. フォームリクエストの定義
// app/Http/Requests/RegisterRequest.php
class RegisterRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed', new StrongPassword],
            'terms' => ['required', 'accepted'],
            'profile_image' => ['nullable', 'image', 'max:2048']
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => '名前を入力してください',
            'email.unique' => 'このメールアドレスは既に登録されています',
            'password.min' => 'パスワードは8文字以上で入力してください',
            'terms.accepted' => '利用規約への同意が必要です'
        ];
    }
}
  1. ビューの実装
{{-- resources/views/auth/register.blade.php --}}
<x-layout>
    {!! Form::open(['route' => 'register', 'method' => 'POST', 'files' => true, 'class' => 'space-y-6']) !!}
        <div class="form-group">
            {!! Form::label('name', '名前', ['class' => 'form-label']) !!}
            {!! Form::text('name', null, [
                'class' => 'form-input',
                'required' => true,
                'autocomplete' => 'name'
            ]) !!}
            <x-form-error name="name" />
        </div>

        <div class="form-group">
            {!! Form::label('email', 'メールアドレス', ['class' => 'form-label']) !!}
            {!! Form::email('email', null, [
                'class' => 'form-input',
                'required' => true,
                'autocomplete' => 'email'
            ]) !!}
            <x-form-error name="email" />
        </div>

        {{-- パスワード入力欄のコンポーネント化 --}}
        <x-password-input />

        <div class="form-group">
            {!! Form::file('profile_image', [
                'class' => 'form-file-input',
                'accept' => 'image/*'
            ]) !!}
            <x-form-error name="profile_image" />
        </div>

        <div class="form-group">
            <label class="flex items-center">
                {!! Form::checkbox('terms', '1', null, ['class' => 'form-checkbox']) !!}
                <span class="ml-2">
                    <a href="{{ route('terms') }}" target="_blank">利用規約</a>に同意する
                </span>
            </label>
            <x-form-error name="terms" />
        </div>

        {!! Form::submit('登録', ['class' => 'btn btn-primary w-full']) !!}
    {!! Form::close() !!}
</x-layout>

問い合わせフォームの実装例

  1. 非同期処理を含むコントローラ
// app/Http/Controllers/ContactController.php
class ContactController extends Controller
{
    public function store(ContactRequest $request)
    {
        $contact = Contact::create($request->validated());

        // 非同期でメール送信とSlack通知
        ContactNotificationJob::dispatch($contact);

        return response()->json([
            'message' => 'お問い合わせを受け付けました',
            'contact_id' => $contact->id
        ]);
    }
}

// app/Jobs/ContactNotificationJob.php
class ContactNotificationJob implements ShouldQueue
{
    public function handle()
    {
        // 管理者へのメール送信
        Mail::to(config('mail.admin'))->send(new NewContactMail($this->contact));

        // Slack通知
        Notification::route('slack', config('services.slack.webhook_url'))
            ->notify(new NewContactNotification($this->contact));
    }
}
  1. フロントエンドの実装
// resources/js/contact-form.js
const submitForm = async (form) => {
    try {
        const response = await axios.post(form.action, new FormData(form));

        showSuccess('お問い合わせを受け付けました');
        trackFormSubmission('contact', response.data.contact_id);
        resetForm(form);

    } catch (error) {
        handleFormError(error);
    }
};

const handleFormError = (error) => {
    if (error.response?.status === 422) {
        showValidationErrors(error.response.data.errors);
    } else {
        showError('送信に失敗しました。時間をおいて再度お試しください。');
    }
};

商品注文フォームの実装例

  1. 複数ステップフォームの実装
// app/Http/Controllers/OrderController.php
class OrderController extends Controller
{
    public function step1(OrderStep1Request $request)
    {
        $validatedData = $request->validated();
        session(['order_step1' => $validatedData]);

        return redirect()->route('order.step2');
    }

    public function step2(OrderStep2Request $request)
    {
        $step1Data = session('order_step1');
        $validatedData = $request->validated();

        DB::beginTransaction();
        try {
            $order = Order::create(array_merge($step1Data, $validatedData));
            $this->processPayment($order);

            DB::commit();
            session()->forget(['order_step1']);

            return redirect()->route('order.complete', $order);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Order processing failed', [
                'error' => $e->getMessage(),
                'data' => array_merge($step1Data, $validatedData)
            ]);

            return back()->withError('注文処理に失敗しました');
        }
    }
}
  1. 注文確認画面の実装
{{-- resources/views/orders/confirm.blade.php --}}
<x-layout>
    <div class="order-summary">
        <h2>注文内容の確認</h2>

        <div class="order-items">
            @foreach($cart->items as $item)
                <div class="item-row">
                    <span>{{ $item->name }}</span>
                    <span>{{ number_format($item->price) }}円</span>
                </div>
            @endforeach
        </div>

        <div class="order-total">
            <span>合計金額</span>
            <span>{{ number_format($cart->total) }}円</span>
        </div>

        {!! Form::open(['route' => 'order.process', 'method' => 'POST', 'id' => 'order-form']) !!}
            <div class="payment-section">
                <h3>お支払い方法</h3>
                @foreach($paymentMethods as $method)
                    <div class="payment-option">
                        {!! Form::radio('payment_method', $method->id) !!}
                        <label>{{ $method->name }}</label>
                    </div>
                @endforeach
            </div>

            {!! Form::submit('注文を確定する', [
                'class' => 'btn btn-primary',
                'data-confirm' => '注文を確定してよろしいですか?'
            ]) !!}
        {!! Form::close() !!}
    </div>
</x-layout>

実装のポイント一覧:

ユースケース重要な実装ポイント
会員登録– 強力なバリデーション
– パスワードポリシー
– プロフィール画像の最適化
問い合わせ– 非同期処理
– 通知の統合
– スパム対策
商品注文– トランザクション管理
– 在庫確認
– 支払い処理の統合

これらの実装例は、実務で必要となる様々な要件に対応できるベースとなります。プロジェクトの要件に応じて、適切にカスタマイズして使用してください。