`validatorjs` と `Valitron` のカスタムルールを作成する

JavaScript 側のバリデーションを validatorjs 、 PHP 側のバリデーションを vlucas/valitron で作成していたのですが、チェックボックスのバリデーションについてカスタムルールを作る必要が出てきたのでメモしておきます。

前提

フォームの例を以下に示します。

<form action="cockadoodledoo" method="post">
    <label for="divinity" class="col-md-3 col-form-label text-right">
        ご祭神
    </label>
    <div class="form-check form-check-inline">
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity0" name="divinity[]" value="天村雲神・天水雲神・天御雲神">天村雲神・天水雲神・天御雲神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity1" name="divinity[]" value="天水分神・国水分神">天水分神・国水分神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity2" name="divinity[]" value="水波女神・水速女神">水波女神・水速女神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity3" name="divinity[]" value="綿津見神">綿津見神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity4" name="divinity[]" value="大己貴神">大己貴神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity5" name="divinity[]" value="阿須波神・波比岐神">阿須波神・波比岐神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity6" name="divinity[]" value="高皇産霊神">高皇産霊神
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity7" name="divinity[]" value="伊邪那岐命・伊邪那美命">伊邪那岐命・伊邪那美命
        </label>
        <label class="form-check-label">
            <input type="checkbox" class="form-check-input" id="divinity8" name="divinity[]" value="天御中主神">天御中主神
        </label>
    </div>
</form>

普通のチェックボックスですね。

ただし、今回はこれをそのまま処理するわけではありません

確認画面をモーダルで済ませたかったので、 JavaScript で一度チェックの入った値を取得し、カンマ区切りの文字列として保持、それを hidden パラメータにセットする、ということをしています。

つまり、実際は以下のような形でセットされます。

<form action="cockadoodledoo" method="post">
  <label for="divinity" class="col-md-3 col-form-label text-right">ご祭神</label>
  <div class="form-check form-check-inline">
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity0" name="divinity[]" value="天村雲神・天水雲神・天御雲神">天村雲神・天水雲神・天御雲神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity1" name="divinity[]" value="天水分神・国水分神">天水分神・国水分神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity2" name="divinity[]" value="水波女神・水速女神">水波女神・水速女神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity3" name="divinity[]" value="綿津見神">綿津見神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity4" name="divinity[]" value="大己貴神">大己貴神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity5" name="divinity[]" value="阿須波神・波比岐神">阿須波神・波比岐神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity6" name="divinity[]" value="高皇産霊神">高皇産霊神
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity7" name="divinity[]" value="伊邪那岐命・伊邪那美命">伊邪那岐命・伊邪那美命
    </label>
    <label class="form-check-label">
      <input type="checkbox" class="form-check-input" id="divinity8" name="divinity[]" value="天御中主神">天御中主神
    </label>
  </div>
</form>

この値をバリデーションしたい、と。

validatorjs

まずは JavaScript 側。 script タグで本体読み込み済みとします。

//custom validatorjs / checkbox
Validator.register('checkbox', function (value, requirement, attribute) {
    const valArray = value.split(',');
    for (const val of valArray) {
        if (requirement.indexOf(val) === -1) {
            return false;
        }
    }
    return true;
}, ':attribute は無効です。');
//generate instance
const validatorjs = new Validator(arrayData, rules);
  • register メソッドでカスタムルールを登録
  • 第一引数がルール名、第二引数にバリデーションの処理、第三引数がエラーメッセージ
  • バリデーション処理の戻り値は true or false
  • エラーメッセージのプレースホルダーは :attribute

となっています。

今回は必須は標準の required に任せて、最初にカンマ区切りの文字列を配列に戻した後に、「各値( val )が想定される値の配列( requirement )に含まれるかどうか」をチェックしています。

Valitron

続いて PHP 側。

//custom valitron / checkbox
\Valitron\Validator::addRule('checkbox', function ($field, $value, array $params, array $fields) {
    $valArray = explode(',', $value);
    for ($i = 0; $i < count($valArray); $i++) {
        if (!in_array($valArray[$i], $params[0], true)) {
            return false;
        }
    }
    return true;
}, '{field}が無効な値です。');
//generate instance
$v = new \Valitron\Validator($post);
  • addRule メソッドでカスタムルールを登録
  • 第一引数がルール名、第二引数にバリデーションの処理、第三引数がエラーメッセージ
  • バリデーション処理の戻り値は true or false
  • エラーメッセージのプレースホルダーは {field}
  • バリデーション処理の引数は以下の通り
    • $field: フィールドのキー名(イメージとしては name 属性に指定された $_POST のキー)
    • $value: 入力値
    • $params: 要件。想定される値の配列

全体的な使い方は validatorjs と似ています。

やや引っかかったのは要件が配列に入っているので、アクセスの際は $params ではなく $params[0] を使うこと。

JavaScript 側と同様、必須要件は required に任せて「各値( $valArray[$i] )が想定される値の配列( $params[0] )に含まれるかどうか」のチェックに専念しています。

ちなみに、 Valitron は subset というルールが標準で存在するためこれが使えそうだと思ったのですが、思うように動かなかったのでカスタムルールを作成することにしました。

備考

今回の JavaScript での値の取得(input type="checkbox" から 文字列へ)は以下のような処理で実装しました( jQuery 使用)。

let arrayData = [];
let strArrayCheckbox = '';
$(`input[name="divinity[]"]:checked`).each(function (index) {
    strArrayCheckbox += $(this).val();
    if (index < $(`input[name="divinity[]"]:checked`).length - 1) {
        strArrayCheckbox += ',';
    }
});
arrayData['divinity'] = strArrayCheckbox;

一応メモ。

参考

validatorjs

Valitron

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。