LaravelにCloudflare Turnstileを実装してみた2

2025.12.12 10:00
2025.12.18 12:25
LaravelにCloudflare Turnstileを実装してみた2

前回は、Cloudflare Turnstile の概要と、
Laravel(カスタムパッケージ構成)で利用するための
設定ファイルやバリデーションルールの作成までを整理しました。

今回はその続きとして、

  • Bladeコンポーネント化
  • サービスプロバイダーへの登録
  • FormRequest への組み込み
  • 実際のフォームへの設置

までをまとめます。

「フォーム側のコードをできるだけ汚さずに組み込みたい」
という前提で進めています。

1. Bladeコンポーネントを作成

フォーム側では、
<x-turnstile /> を置くだけで済むようにしたかったため、
Turnstile 用の Blade コンポーネントを作成します。

JavaScript の読み込みや data 属性の指定を
毎回フォーム側で書くのは避けたかった、という理由もあります。

<?php

namespace Tool\General\Application\Infrastructure\View\Components;

use Illuminate\View\Component;

class TurnstileWidget extends Component
{
    public $theme;
    public $size;

    public function __construct($theme = 'light', $size = 'normal')
    {
        $this->theme = $theme;
        $this->size  = $size;
    }

    public function render()
    {
        return view('general::components.turnstile');
    }
}

theme や size は、
必要になったときに Blade 側から切り替えられるように
引数として受け取る形にしています。

この形にしておくと、

  • フォーム側の記述がかなりシンプルになる
  • Turnstile 固有の実装をコンポーネントに閉じ込められる

というメリットがあります。

JavaScript を直接意識せずに置けるので、
フォーム側の見通しはかなり良くなりました。

2. サービスプロバイダーに登録

次に、設定ファイルと Blade コンポーネントを
カスタムパッケージのサービスプロバイダーで登録します。

今回は
Tool/General/GeneralServiceProvider.php
でまとめて行っています。

useで読み込み

use Illuminate\Support\Facades\Blade;
use Tool\General\Application\Infrastructure\View\Components\TurnstileWidget;

register / boot に実装

public function register()
{
    $this->mergeConfigFrom(
        config_path('turnstile.php'),
        'turnstile'
    );
}

public function boot()
{
    // Turnstile Bladeコンポーネント登録
    Blade::component('turnstile', TurnstileWidget::class);

    // Turnstileヘルパーの読み込み
    if (file_exists(__DIR__.'/Helpers/turnstile.php')) {
        require_once __DIR__.'/Helpers/turnstile.php';
    }
}

カスタムパッケージ構成のため、
mergeConfigFrom() を使って設定をマージしています。

また、turnstile_enabled() のような
ヘルパー関数を使いたかったため、
ヘルパーファイルもこのタイミングで読み込んでいます。

3. FormRequestにルールを追加

次に、既存の FormRequest に
Turnstile のバリデーションを 条件付きで追加します。

    $rules = [
        'name' => 'required|string|max:255',
        'kana' => 'string|max:255',
        'tel' => 'string|max:50',
        'email' => 'required|string|email|min:5|max:255',
    ];
    
    // Turnstileバリデーション追加
    if (turnstile_enabled()) {
        $rules['cf-turnstile-response'] = ['required', new Turnstile()];
    }
    
    return $rules;
  • ここで意識したポイントは以下です。
  • 保存処理は通常どおり実行する
  • Turnstile が失敗した場合は
    コントローラに入る前に バリデーションで止める
  • ローカル環境やテスト環境では
    turnstile_enabled() で簡単に無効化できる
  • Bot 対策は重要ですが、
  • アプリケーションの本質的な処理とは切り離しておきたいので、
  • この形に落ち着きました。

4. フォームに組み込む

最後に、フォーム側への組み込みです。
Blade 側は本当にこれだけです。

<form action="{{ url('form/submit') }}" method="POST">
    @csrf

    <!-- 各種フォーム項目 -->

    <x-turnstile />

    <button type="submit">送信</button>
</form>

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

フォームの HTML を見ても、
Turnstile の存在がほとんど主張してこないのが
個人的にはかなり好印象でした。

ユーザー側でも
「チェックさせられている感」がほぼなく、
UX の面でも reCAPTCHA より軽い印象です。

今回は、

  • Blade コンポーネント化
  • サービスプロバイダー登録
  • FormRequest への組み込み
  • フォーム設置

までを整理しました。

次回は、

  • ローカル環境・CIでのテスト方法
  • 成功/失敗パターンの考え方
  • Feature テストでの扱い

あたりをもう少し詳しく書く予定です。

今回は以上です!