バリデーション#
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」。バリデーションは単独で使うより、この型で使うのが基本。
改訂履歴 (4)
- v1.1.0: usePHP→Rosanna(rosanna-defer)に合わせて更新
- v0.31.0: satisfies() を追記、required() に修正
- v0.17.0 の i18n でバリデーション既定メッセージがローカライズされる旨をクロスリンク追記
- 新規作成