それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

WEBフォーム項目の連動項目でのバリデーションの一般的な解決法(laravelでの実装例)

Selectやラジオボタンのチェック項目よって、バリデーションが異なる場合。

根本問題としてそういうフォームを作るなと思うのですが。

直前の選択項目によって次の入力項目のバリデーションが異なることがある。

例 電話番号

固定電話と携帯電話で番号をバリデーションする場合

例として電話番号のバリデーションを考えてみる

HTMLの例

<select name='tel.type' >
  <option value=''>--選んで--</option>
  <option value='cellular'>携帯</option>
  <option value='home'>自宅</option>
</select>
<input type=tel name='tel.number' >

フォーム項目の連動(選択肢よって入力制限が異なる)場合の例として自宅・携帯によって電話番号のバリデーションを変えたいとする。

バリデーションの例

<?php
$validator = Validator::make(
  ['tel' => [
    'type' => 'cellular',
    'number' => '080-1234-5678',],
  ],
  [
    'tel.type' => ['required'],
    'tel.number' => ['required'],
    'tel' => ['required',
      function( $key, $value ) {
        [$type, $number]=array_values( $value );
        
        if ( $type == 'cellular' ) {
          $exp = '/0[789]0-\d{4}-\d{4}/';
        } else if ( $type == 'home' ) {
          $exp = '/0\d{2,3}-\d{2,4}-\d{4}/';
        }
        return preg_match( $exp, $number );
      }],
  ] );

$ret = $validator->passes();

ポイント:グループ化

ポイントは超単純。フォーム項目を「グループ」に分ける。

今回は、フォーム項目をtel配列に入れている。配列に入れるとバリデーションは格段に扱いやすくなる。

そしてグループ(配列)ごとに、バリデーションすると考える。

バリデーションの項目をrequired_if などであれこれ考えるより、配列で綿たほうが早い。

バリデーションのネスト。

コールバックを用いてバリデーションしている箇所が、if 連鎖で美しくないと思う人もいるだろう。

そういうときは、バリデーションをネストしてあげる。

コールバックで難読化してるのを

<?php
[
  'tel.type' => ['required'],
  'tel.number' => ['required'],
  'tel' => ['required',
    function( $key, $value ) {
      [$type, $number] = array_values( $value );
      if ( $type == 'cellular' ) {
        return preg_match( '/0[789]0-\d{4}-\d{4}/', $number );
      } else if ( $type == 'home' ) {
        return preg_match( '/0\d{2,3}-\d{2,4}-\d{4}/', $number );
      }
    }],
]

スッキリ(ネスト)させる

<?php
'tel' => ['required', function( $key, $value ) {
  $validator = Validator::make( $value, [
    'type' => 'required',
    'number' => ['required', new PhoneRule( $value['type'] )],
  ] );
  return $validator->passes();
}]

相当スッキリするはずである。

まとめ

要は、「配列」(パーツ)にフォーム項目を分けて、ネストしてパーツごとにバリデーションするとスッキリする。

あれこれ、Ruleを考えたり、require_if などを組み合わせるより圧倒的にかんたんで読みやすい。検索しても出てこないのが不思議である。

今回は、laravelにおけるバリデーションであったが、railsでもjavaのspringやpython djangophp のその他のフレームワーク(cakephp)などでも全く同じ考え方で処理できると思うんですよね。バリデーションが複数にまたがるときも「分割統治する」という分割責任原則が通用する。

バリデーションが複数個所で入力値によって変わるときどうするのかという質問は頻発なのになんでみんな複雑なものを複雑にコーディングしてしまうんだろう。