PHP
WordPress
Validation
ContactForm7

Contact Form 7 のカスタムバリデーションの書き方(カスタマイズ方法)

Wordpressのコンタクトフォームと言えば、検索順位でも人気でも間違いなくNo.1なのが Contact Form 7。いまさら説明の必要もない、日本を代表するプラグイン。なのでドキュメントも充実!と思いきや、意外と欲しい情報に対するまとまったドキュメントがなかったので、せっかくなのでまとめさせていただきます。
テーマは、ひらがな判定、電話番号の桁数判定、はたまたAをチェックしたらB必須など、様々なカスタムバリデーションをどう書くか!? その1つのご提案です。

公式ドキュメント

https://contactform7.com/2015/03/28/custom-validation/
え、英語だ!!

結論

使用しているテーマの、functions.php に書きます。下記がそのサンプルです。

functions.php
/* ContactForm7 のカスタムバリデーション */
add_filter('wpcf7_validate', 'wpcf7_validate_customize', 11, 2);

function wpcf7_validate_customize($result,$tags){

  foreach( $tags as $tag ){
    $type = $tag['type'];
    $name = $tag['name'];
    $post = trim(strtr((string) $_POST[$name], "\n", ""));

    switch($type){
      case 'text':
      case 'text*':
        if(preg_match('/.*-kana$/', $name )){
          if(preg_match("/^[ぁ-ん]+$/u",$_POST[$name]) == false){
            $result->invalidate( $name,'全角ひらがなで入力してください。' );
          }
        }
        break;
      case 'tel':
      case 'tel*':
        if( preg_match( '/^0[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}$/',$_POST[$name] ) == false){
          $result->invalidate( $name,'ハイフンは省略せず市外局番からご記入ください。' );
        }
        break;
    }

  }
  return $result;
}

解説

ポイント1:フックは1つ

検索すると、例えば下記のような、フォームエレメントのTypeに応じたフックが紹介されることが多いです。この場合は、textタイプのエレメントです。

add_filter('wpcf7_validate_text',  'wpcf7_validate_kana', 11, 2);
add_filter('wpcf7_validate_text*', 'wpcf7_validate_kana', 11, 2);
function wpcf7_validate_kana($result,$tag){
  $type = $tag['type'];
  $name = $tag['name'];
  /* ... 略 ... */
}

ただ、これだとエレメントの種類が増えるとそれだけフックの数も増やす必要があり、コード量が増えるし書き漏れのミスも増えます。必須かどうかで2つ書くのもナンセンス。なんとかならないのかなぁと思ってソースコードを見ていたら、ありました。

wp-content\plugins\contact-form-7\includes\submission.php
class WPCF7_Submission {

  /* ... 略 ... */

  private function validate() {

    /* ... 略 ... */

    $result = new WPCF7_Validation();

    $tags = $this->contact_form->scan_form_tags();

    foreach ( $tags as $tag ) {
      $type = $tag['type'];
      $result = apply_filters( "wpcf7_validate_{$type}", $result, $tag );
    }

    $result = apply_filters( 'wpcf7_validate', $result, $tags );

    /* ... 略 ... */

  }

  /* ... 略 ... */
}

バリデーションの際のフックは、各エレメントに対して "wpcf7_validate_{$type}" が呼ばれた後に、 'wpcf7_validate' というフックが呼ばれているんですね。これを使わせていただきます。注意点は、 $tag が配列の $tags として与えられるので、中でループ展開をしているところです。

add_filter('wpcf7_validate', 'wpcf7_validate_customize', 11, 2);

function wpcf7_validate_customize($result,$tags){
  foreach( $tags as $tag ){
    $type = $tag['type'];
    $name = $tag['name'];
  /* ... 略 ... */
  }
}

ポイント2:switchで分けて、素直に if で判定

ここは好みが別れるところかもしれません。個人的には、switchで大きくタイプ別に分けて、ifで詳しい条件を書いていくのが好きです。

    switch($type){
      case 'text':
      case 'text*':
        if(preg_match('/.*-kana$/', $name )){
          if(preg_match("/^[ぁ-ん]+$/u",$_POST[$name]) == false){
            /* ... 略 ... */
          }
        }
        break;
      case 'tel':
      case 'tel*':
        if( preg_match( '/^0[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}$/',$_POST[$name] ) == false){
            /* ... 略 ... */
        }
        break;
    }

そもそも Type で条件をかき分ける必要はなく(たとえば「ひらがな判定」は input[type=text]だけでなく textarea などにも使いたい)、だったら case に条件式を書いてしまう、という手もあります。

    switch(true){
      case preg_match('/.*-kana$/', $name ) && 
           preg_match("/^[ぁ-ん]+$/u",$_POST[$name]) == false :
           /* ... 略 ... */
           break;
      case preg_match('/^tel\*?$/', $type ) &&
           preg_match( '/^0[0-9]{1,3}-[0-9]{2,4}-[0-9]{3,4}$/',$_POST[$name] ) == false :
           /* ... 略 ... */
           break;
    }

ポイント3:入力された値……それすなわち $_POST

正直、ちょっとどうにかならないのかなぁと思ったところ。あまり$_POSTといったグローバル変数を直接参照するのは良くないし、Wordpressにもそれなりの手段が用意されていますが、Contact Form 7のバリデーションでは常套手段のようなのでスルーします。こういうものだと思うことにします。

ポイント4:結果は $result->invalidate を使って返す

Contact Form 7のversionが 4.1 に上がったら、エラーチェックがスルーされるようになった
http://blog.valid-seo.biz/2015/02/1399.html

こちらに紹介されているように、Google先生が時々教えてくれる方法が、今はもう使えない、ということがあるようです。今も使える手段として、$result->invalidate を使います。

     $result->invalidate( $name,'全角ひらがなで入力してください。' );

この1行だけ。これを1回でも実行すると、バリデーションは失敗し、$name に対する「全角ひらがなで入力してください。」というメッセージがフォームに表示されます。先のソースコードを見ると、WPCF7_Validation クラスのインスタンスですね。

感想

いまやノンプログラミングで、GUIでがしがしとバリデーションが組めるコンタクトフォームプラグインがある中、ほぼ100%ピュアなPHPでバリデーションを組まされることになるなんて(しかもキレイに書ける)、なかなか硬派なプラグインだと思いました。良くも悪くも上級者向け、なんでしょうか。逆に、複数の項目を横断的に判断する条件(AがチェックされているときだけBが必須…とか)もカンタンに書けるので、コーダーとしては腕が鳴る、いいプラグインだと思います。

あと、JavaScriptで画面遷移せずバリデーションエラーを出すので、バリデーションもJavaScriptなのかと思ったら、AjaxでサーバーサイドのPHPに判定させているっていうのも、PHPer想いの硬派な設計なんですね!(・∀・)