Relayer
Docs機能validation

バリデーション#

Zod に着想を得たスキーマバリデータで、フィールド別エラーを返します。実用上はほぼ常に フォーム送信=サーバーアクション の中で使うので、まず単体 API を押さえ、その後に関数スタイルページでの実践フロー(送信→検証→失敗なら再描画/成功なら PRG)を示します。

スキーマ宣言#

use Polidog\Relayer\Validation\Validator;

$schema = Validator::object([
    'email' => Validator::string()->trim()->email(),
    'name'  => Validator::string()->trim()->required('名前は必須です。'),
    'age'   => Validator::int()->min(0)->optional(),
]);

利用できる型#

string() / int() / float() / bool() / enum() / object() / array()、および email() / url() などの制約。

string()->trim()->min(1) のようにメソッドチェーンで制約を積み、 optional() で任意フィールドにします。各エラーメッセージは制約メソッドの第 2 引数で上書きできます。

パース(safeParse / ParseResult)#

safeParse() は例外を投げず ParseResult を返します。

$result = $schema->safeParse($input);

if ($result->success) {
    $data = $result->data;     // 検証済み・型付きデータ
} else {
    $errors = $result->errors; // ['email' => 'メッセージ', ...]
}

入力($input)はスーパーグローバルではなく、サーバーアクションが渡す $form 配列、または Request から取った値を渡します(規約: ページ/ハンドラで $_POST を直接読まない)。

関数スタイルページでの実践フロー#

バリデーションの典型は「フォーム送信 → 検証 → 失敗ならエラー付きで同じページを再描画 / 成功なら PRG リダイレクト」です。関数スタイルページのファクトリはリクエスト毎に走るので、$errors を参照キャプチャしてハンドラと描画クロージャで共有できます(サーバーアクション / 関数スタイルページ も参照)。

use Polidog\Relayer\Validation\Validator;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\Rosanna\Runtime\Element;

return function (PageContext $ctx): Closure {
    $errors = [];
    $old = ['name' => '', 'email' => ''];

    $save = $ctx->action('save', function (array $form) use ($ctx, &$errors, &$old) {
        $old = $form;
        $result = Validator::object([
            'name'  => Validator::string()->trim()->required('名前は必須です。'),
            'email' => Validator::string()->trim()->email('メール形式が不正です。'),
        ])->safeParse($form);

        if (!$result->success) {
            $errors = $result->errors;   // 描画クロージャで表示。再描画される
            return;
        }

        // $result->data を保存して Post/Redirect/Get
        $ctx->redirect('/thanks');
    });

    return function () use ($save, &$errors, &$old): Element {
        return <form action={$save}>
            <label>
                名前
                <input name="name" value={$old['name'] ?? ''} />
            </label>
            {isset($errors['name'])
                ? (<p className="error">{$errors['name']}</p>)
                : null}

            <label>
                メール
                <input name="email" value={$old['email'] ?? ''} />
            </label>
            {isset($errors['email'])
                ? (<p className="error">{$errors['email']}</p>)
                : null}

            <button>送信</button>
        </form>;
    };
};

ポイント:

  • CSRF はサーバーアクションが自動付与(サーバーアクション)。
  • ハンドラは render() の前に走るので、return; すればそのまま $errors 入りでフォームが再描画される。成功時のみ $ctx->redirect() で PRG。
  • エラーメッセージは Rosanna が自動エスケープして描画(Rosanna)。
  • 制約メソッドの既定メッセージは relayer.* カタログでローカライズされ、リクエストの解決ロケールに追従します(多言語化)。 refine() / required('…') に渡した独自メッセージはそのまま透過。
  • クラススタイルなら #[Auth] 同様、PageComponent::action() で同じ流れ。

意味的なチェック(TypeSafe Jev)#

satisfies($judge, $condition, $message, $threshold = 0.5) は、コードでは書けない検証(「スパムか」「本当に問い合わせ内容か」)を行います。値は state {"value": …} として Judge に渡り、条件が成り立つ確率が $threshold 以上なら通過します。

TYPESAFE_API_KEY(任意で TYPESAFE_MODEL、既定 jev-latest)を設定すると Polidog\Relayer\Validation\Judge が JevJudge にバインドされ、未設定なら登録されません。

use Polidog\Relayer\Validation\Judge;

$schema = Validator::object([
    'body' => Validator::string()->trim()->min(10)
        ->satisfies($judge, '`value` is a genuine support question, not spam or ads', 'スパムと判定されました。', 0.7),
]);
  • 安いチェック(min など)は satisfies() の前に置きます。手前で落ちればリモート呼び出しは発生しません。
  • 呼び出しごとにネットワーク往復とトークン課金が発生します。閾値は自分のデータで調整してください。
  • API の失敗は safeParse() / parse() から JudgeException になります。障害時にフォームを止めるかはアプリ側で catch して決めます。
  • 別のバックエンドは Judge::probability(string $condition, mixed $state): float を実装します。

まとめ#

  • 単体: Validator::object([...])->safeParse($input) → ParseResult (success / data / errors)。
  • 実践: 関数スタイルページ+サーバーアクションで「失敗→再描画 / 成功→PRG」。バリデーションは単独で使うより、この型で使うのが基本。
最終更新 — 2026-11-04
改訂履歴 (4)
  • v1.1.0: usePHP→Rosanna(rosanna-defer)に合わせて更新
  • v0.31.0: satisfies() を追記、required() に修正
  • v0.17.0 の i18n でバリデーション既定メッセージがローカライズされる旨をクロスリンク追記
  • 新規作成