Examples - WordPress
WordPress プラグインとして有効化した環境での実装例です。schv_* ヘルパー関数を利用します。
ソースコード:
packages/example/wordpress/
1. 基本的なバリデーション
schv_validator() でバリデーターを生成し、template_redirect フックでフォーム送信を処理します。
php
<?php
/**
* WordPress example: Basic validation
*
* Usage in a theme's functions.php or a custom plugin.
* Requires the Schemable Validator plugin to be active.
*/
use Respect\Validation\Validator as v;
// Handle form submission (call from 'template_redirect' action)
function my_handle_contact_form(): void {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['my_contact_submit'])) {
return;
}
$schema = [
'name' => v::stringType()->length(1, 50),
'email' => v::email(),
'body' => v::stringType()->length(1, 1000),
];
$validator = schv_validator($schema);
$result = $validator->validate($_POST)->getResult();
$all_valid = array_reduce($result, fn($carry, $item) => $carry && $item['is_valid'], true);
if ($all_valid) {
schv_form()->save($result);
wp_redirect(home_url('/contact-complete/'));
exit;
}
// Store errors for display
global $my_contact_result;
$my_contact_result = $result;
}
add_action('template_redirect', 'my_handle_contact_form');
// Render form (use as shortcode or in template)
function my_render_contact_form(): string {
global $my_contact_result;
ob_start();
?>
<form method="post">
<?php foreach (['name', 'email', 'body'] as $field): ?>
<?php $error = $my_contact_result[$field]['errors'] ?? null; ?>
<div>
<label><?php echo esc_html($field); ?></label>
<?php if ($field === 'body'): ?>
<textarea name="<?php echo esc_attr($field); ?>"><?php echo esc_textarea($my_contact_result[$field]['value'] ?? ''); ?></textarea>
<?php else: ?>
<input type="text" name="<?php echo esc_attr($field); ?>" value="<?php echo esc_attr($my_contact_result[$field]['value'] ?? ''); ?>">
<?php endif; ?>
<?php if ($error): ?><p class="error"><?php echo esc_html($error); ?></p><?php endif; ?>
</div>
<?php endforeach; ?>
<button type="submit" name="my_contact_submit">Submit</button>
</form>
<?php
return ob_get_clean();
}2. スキーマエディタの定義とコードのマージ
スキーマエディタ(管理画面)で定義したプリミティブフィールドと、GUI では表現できないロジック(ファイルアップロード、条件付き必須、カスタムバリデーション、ドライバ注入)を mergeJsonSchema() で組み合わせます。
php
use SchemableValidator\SV;
use SchemableValidator\Adapters\Captcha\ReCaptchaV3Driver;
use SchemableValidator\Adapters\Native\NativeImageDriver;
// 1. スキーマエディタで作成したスキーマを読み込む(スラッグ: "contact")
$gui = schv_stored_schema('contact')->toJsonSchema();
// 2. コード側のフィールドと条件を追加してマージ
$schema = SV::object([
'avatar' => SV::file(['image/jpeg', 'image/png'], ['maxWidth' => 4096]),
'company_name' => SV::string()->min(1)->max(200)->optional(),
])->mergeJsonSchema($gui)
->when('type', SV::equal('company'), ['company_name']);
// 3. ドライバを指定してバリデーション
$result = $schema
->toValidator([
'imageDriver' => new NativeImageDriver(),
'captchaDriver' => new ReCaptchaV3Driver('YOUR_SECRET'),
])
->validate($_POST)
->validateFiles($_FILES)
->validateCaptcha()
->getResult();GUI で定義したフィールド(name、email、type)と、コードで定義したフィールド(avatar、company_name)がまとめて検証されます。 同名のフィールドが両方に存在する場合、コード側の定義が優先されます。
マージしたスキーマをクライアント向けに REST エンドポイントとして公開するには以下のようにします。
php
schv_register_schema('/contact', schv_stored_schema('contact'));3. ファイルアップロードのバリデーション
validateFiles() で $_FILES を検証し、許容する MIME タイプを制限します。
php
<?php
/**
* WordPress example: File upload validation
*/
use Respect\Validation\Validator as v;
function my_handle_upload_form(): void {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['my_upload_submit'])) {
return;
}
$schema = [
'attachment' => v::key('error', v::equals(UPLOAD_ERR_OK))
->key('name', v::oneOf(v::extension('jpg'), v::extension('png'))),
];
$result = schv_validator($schema)->validateFiles($_FILES)->getResult();
global $my_upload_result;
$my_upload_result = $result;
$all_valid = !empty($result['attachment']) && $result['attachment'][0]['is_valid'];
if ($all_valid) {
// move_uploaded_file(...) here
wp_redirect(home_url('/upload-complete/'));
exit;
}
}
add_action('template_redirect', 'my_handle_upload_form');
function my_render_upload_form(): string {
global $my_upload_result;
$error = $my_upload_result['attachment'][0]['errors'] ?? null;
ob_start();
?>
<form method="post" enctype="multipart/form-data">
<div>
<label>Attachment (jpg / png)</label>
<input type="file" name="attachment">
<?php if ($error): ?><p class="error"><?php echo esc_html($error); ?></p><?php endif; ?>
</div>
<button type="submit" name="my_upload_submit">Upload</button>
</form>
<?php
return ob_get_clean();
}4. CSRF トークン保護
createToken() で hidden フィールドにトークンを埋め込み、送信時に checkToken() で照合します。
php
<?php
/**
* WordPress example: CSRF token protection
*/
use Respect\Validation\Validator as v;
use SchemableValidator\Security\CsrfGuard;
function my_handle_csrf_form(): void {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['my_csrf_submit'])) {
return;
}
$csrf = new CsrfGuard();
$validator = schv_validator(['message' => v::stringType()->notEmpty()]);
if (!$csrf->checkToken($_POST['csrf_token'] ?? '')) {
wp_die('Invalid CSRF token.', 'Security Error', ['response' => 403]);
}
$result = $validator->validate($_POST)->getResult();
global $my_csrf_result;
$my_csrf_result = $result;
}
add_action('template_redirect', 'my_handle_csrf_form');
function my_render_csrf_form(): string {
global $my_csrf_result;
$csrf = new CsrfGuard();
$token = $csrf->createToken();
ob_start();
?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?php echo esc_attr($token); ?>">
<div>
<label>Message</label>
<input type="text" name="message" value="<?php echo esc_attr($my_csrf_result['message']['value'] ?? ''); ?>">
<?php if (!empty($my_csrf_result['message']['errors'])): ?>
<p class="error"><?php echo esc_html($my_csrf_result['message']['errors']); ?></p>
<?php endif; ?>
</div>
<button type="submit" name="my_csrf_submit">Submit</button>
</form>
<?php
return ob_get_clean();
}5. メールテンプレートレンダリング
schv_template() で WP オプションのテンプレートにバリデーション済みデータを差し込みます。
php
<?php
/**
* WordPress example: Email template rendering
*
* Requires the Schemable Validator plugin active.
* Template body is editable from WP Admin > Settings > Schemable Validator.
*/
function my_send_confirmation_emails(): void {
$form = schv_form();
$data = $form->get();
if (!$data) {
return;
}
// $option_keys comes from Plugin::keysAll() — see 02-feature-guide.md for details.
// Here we use hardcoded keys matching the Plugin constructor defaults.
$template = schv_template([
'aliases' => [
'name' => 'name',
'email' => 'email',
'body' => 'body',
],
'templates' => [
'user' => get_option('SCHV_REPLY_FORMAT_FOR_user', ''),
'admin' => get_option('SCHV_REPLY_FORMAT_FOR_admin', ''),
],
]);
$to_user = $data['email']['value'] ?? '';
$to_admin = get_option('admin_email');
if ($to_user) {
wp_mail($to_user, 'Thank you for your inquiry', $template->get('user'));
}
wp_mail($to_admin, 'New inquiry received', $template->get('admin'));
}6. マルチページフォーム(入力 → 確認 → 完了)
schv_form() でセッションにデータを保持し、3ページにまたがるフォームを実装します。
php
<?php
/**
* WordPress example: Multi-page form (input → confirm → complete)
*
* Create three pages with slugs: my-form-input, my-form-confirm, my-form-complete
* Add shortcodes [my_form_input], [my_form_confirm], [my_form_complete] to each page.
*/
use Respect\Validation\Validator as v;
// ── Schema ────────────────────────────────────────────────────────────────────
function my_form_schema(): array {
return [
'name' => v::stringType()->length(1, 50),
'email' => v::email(),
'body' => v::stringType()->length(1, 1000),
];
}
// ── Step 1: Input ─────────────────────────────────────────────────────────────
add_action('template_redirect', function () {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || ($_POST['my_form_step'] ?? '') !== 'input') {
return;
}
$validator = schv_validator(my_form_schema());
$result = $validator->validate($_POST)->getResult();
$all_valid = array_reduce($result, fn($c, $i) => $c && $i['is_valid'], true);
if ($all_valid) {
schv_form()->save($result);
wp_redirect(home_url('/my-form-confirm/'));
exit;
}
global $my_form_input_result;
$my_form_input_result = $result;
});
add_shortcode('my_form_input', function (): string {
global $my_form_input_result;
$r = $my_form_input_result ?? [];
ob_start(); ?>
<form method="post">
<input type="hidden" name="my_form_step" value="input">
<?php foreach (['name', 'email'] as $f): ?>
<p>
<label><?php echo esc_html($f); ?></label>
<input type="text" name="<?php echo esc_attr($f); ?>" value="<?php echo esc_attr($r[$f]['value'] ?? ''); ?>">
<?php if (!empty($r[$f]['errors'])): ?><span><?php echo esc_html($r[$f]['errors']); ?></span><?php endif; ?>
</p>
<?php endforeach; ?>
<p>
<label>body</label>
<textarea name="body"><?php echo esc_textarea($r['body']['value'] ?? ''); ?></textarea>
<?php if (!empty($r['body']['errors'])): ?><span><?php echo esc_html($r['body']['errors']); ?></span><?php endif; ?>
</p>
<button type="submit">Next →</button>
</form>
<?php return ob_get_clean();
});
// ── Step 2: Confirm ───────────────────────────────────────────────────────────
add_action('template_redirect', function () {
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || ($_POST['my_form_step'] ?? '') !== 'confirm') {
return;
}
wp_redirect(home_url('/my-form-complete/'));
exit;
});
add_shortcode('my_form_confirm', function (): string {
$data = schv_form()->get();
if (!$data) {
return '<p>No data. Please <a href="' . esc_url(home_url('/my-form-input/')) . '">start over</a>.</p>';
}
ob_start(); ?>
<dl>
<?php foreach ($data as $field => $state): ?>
<dt><?php echo esc_html($field); ?></dt>
<dd><?php echo esc_html($state['value']); ?></dd>
<?php endforeach; ?>
</dl>
<form method="post">
<input type="hidden" name="my_form_step" value="confirm">
<a href="<?php echo esc_url(home_url('/my-form-input/')); ?>">← Back</a>
<button type="submit">Submit</button>
</form>
<?php return ob_get_clean();
});
// ── Step 3: Complete ──────────────────────────────────────────────────────────
add_shortcode('my_form_complete', function (): string {
schv_form()->clear();
return '<p>Thank you! Your message has been received.</p>';
});