概要
ContactForm7(以下、CF7)で作った問い合わせフォームでは、 wpcf7_validate
をフックしてバリデーションを実施できます。詳細は以下参照。
ここでの問題点
以下のような場合、厄介なことになりそう(なった)
- CF7で作成したフォームが同一サイト内で複数存在する
- フォームを複製作成してしまい、name値が同じでも、inputの種類や選択項目が違う
- チェックボックスなどの選択項目の内容が、頻繁に更新される
特に、選択項目の評価基準となるものがバリデーション関数内にベタ書きだと、ここの処理も更新作業が発生します。
そのため、常に最新のフォームから選択項目を参照する必要があります。
add_filter('wpcf7_validate', 'my_form_validate', 11, 2);
function my_form_validate( $result, $tags ) {
$form = WPCF7_Submission::get_instance();
if($form) {
foreach( $tags as $tag ) {
$type = $tag['type'];
$name = $tag['name'];
$post_data = $_POST[$name];
switch($type) {
case 'text':
case 'text*':
//何か処理入れる
break;
case 'select':
case 'select*':
$list = ['AAA', 'BBB', 'CCC'];
// ↑選択項目と同じ項目リスト。問い合わせフォームが更新されたら修正
if( !in_array( $post_data, $list ) ){
$result->invalidate($name, "エラー" );
}
break;
}
}
}
return $result;
}
Formデータを取得
ということで、フォームを表示するためのフォームデータを取得し、そこから選択項目などを引っ張ってきます。
CF7のカスタムバリデーションの記事を参考に判定処理を書くとこんな感じに(関数名が長い&ダサいので、適宜変更を推奨)。
まとめたコード
// CF7の投稿IDからフォームデータを返す
function my_get_wpcf7_form_values_by_id( $id ) {
$form = wpcf7_contact_form($id);
$res = [];
if($form) {
$manager = WPCF7_FormTagsManager::get_instance();
$form = $form->prop( 'form' );
$form = $manager->replace_with_placeholders( $form );
$form = $manager->restore_from_placeholders( $form );
$form = $manager->replace_all( $form );
$res = $manager->get_scanned_tags();
}
return $res;
}
// inputのname値から選択項目を返す
function my_get_form_values_by_name( $name, $tags ) {
$res = [];
if ( !is_array($tags) ) return $res;
if($tags) {
foreach( $tags as $tag ) {
if( $tag->name == $name) {
$res = $tag->values;
}
}
}
return $res;
}
// バリデーション処理
add_filter('wpcf7_validate', 'my_form_validate', 11, 2);
function my_form_validate( $result, $tags ) {
$form = WPCF7_Submission::get_instance();
if($form) {
$contact_form = WPCF7_ContactForm::get_current();
$contact_form_id = $contact_form->id;
$contact_tags = my_get_wpcf7_form_values_by_id($contact_form_id);
$invalid_required = '必須項目に入力してください。';
$invalid_input = '入力内容に誤りがあります。';
foreach( $tags as $tag ) {
$type = $tag['type'];
$name = $tag['name'];
$post_data = $_POST[$name];
$option_values = my_get_form_values_by_name($name, $contact_tags);
switch($type) {
case 'text':
case 'text*':
// require
if( $type == 'text*' && $post_data == '') {
$result->invalidate( $name, $invalid_required);
}
// TODO: その他処理追加
break;
case 'select':
case 'select*':
case 'radio':
case 'radio*':
if( in_array( $type, array('select*','radio*') ) && $post_data == '' ) {
$result->invalidate( $name, $invalid_required );
}
if( !in_array( $post_data, $option_values ) ) {
$result->invalidate( $name, $invalid_input );
}
break;
case 'checkbox':
case 'checkbox*':
if( $type == 'checkbox*' && is_null($post_data) ) {
$result->invalidate( $name, $invalid_required );
}
if($post_data) {
foreach( $post_data as $data ) {
if( !in_array( $data, $option_values ) ) {
$result->invalidate( $name, $invalid_input );
}
}
}
break;
case 'email':
case 'email*':
if( $type == 'email*' && $post_data == '' ) {
$result->invalidate( $name, $invalid_required);
}
if( !empty($post_data) && !is_email($post_data) ) {
$result->invalidate( $name, $invalid_input );
}
break;
case 'tel':
case 'tel*':
if( $type == 'tel*' && $post_data == '' ) {
$result->invalidate( $name, $invalid_required);
}
if( !empty($post_data) && !wpcf7_is_tel( $post_data ) ) {
$result->invalidate( $name, $invalid_input );
}
break;
case 'textarea':
case 'textarea*':
if( $type == 'textarea*' && $post_data == '' ) {
$result->invalidate( $name, $invalid_required );
}
break;
case 'number':
case 'number*':
if( $type == 'number*' && $post_data == '' ) {
$result->invalidate( $name, $invalid_required );
}
if ( !empty($post_data) && !wpcf7_is_number($post_data) ) {
$result->invalidate( $name, $invalid_input );
}
break;
case 'date':
case 'date*':
if( $type == 'date*' && $post_data == '' ) {
$result->invalidate( $name, $invalid_required );
}
if ( !empty($post_data) && !wpcf7_is_date($post_data) ) {
$result->invalidate( $name, $invalid_input );
}
break;
}
}
}
return $result;
}
ポイントなど
$_POSTからが推奨らしい
バリデーション系の記事を読み漁っていくと、WPCF7_Submission
クラスのget_posted_data()
で送信内容を入れるパターンもありましたが、これはプラグイン内でサニタイズしたデータだそうです。
CF7も生の$_POSTで検証を奨めていたので、そちらを採用しました。
選択項目のバリデーション
my_get_form_values_by_name($name, $contact_tags);
で選択項目が配列で返ってくるので、ベタ書きしたリスト配列から置き換え。
※CF7は標準でjsでのバリデーションを行っているようですが、所謂「悪意あるユーザー」対策として、選択項目に対しても厳密なバリデーションを行っています。
if( !in_array( $post_data, $option_values ) ) {
$result->invalidate( $name, $invalid_input );
}
判定処理の関数
ここは個人の好みの問題だったり、状況に応じて書いていただきたいところですが、今回はWPとCF7内にあったバリデーション関数を使ってみました(長いものには巻かれろ精神)。
is_email($post_data)
wpcf7_is_tel( $post_data )
-
wpcf7_is_date($post_data)
など