FormValidator::LazyWay で検証ルールをまとめようB!

どうも、亀仙人になって鼻血ブーしたい vkgtaro です。ご機嫌いかがですか。亀仙人が最初なのかどうか知らないけど、鼻血を吹くというマンガ表現はすごく好きです。おっと、hacker track でした。
ということで僕もハッカーではないけれど、自分がメンテナンスしてるモジュールの中から一つ紹介させてもらいますね。

今日は FormValidator::LazyWay で検証ルールをまとめちゃいます。

フォームの検証

web アプリケーションを作っていると、どのページでも出てくる入力項目ってあると思います。

会員登録時のパスワード項目とログイン時のパスワード項目とユーザ情報変更時のパスワード項目と退会時に求められるパスワード入力項目、リマインダで再設定するパスワード入力項目……などなど。

これらの項目がフォームごとにルールを指定してると、登録時は半角英数字と記号のみで6文字から14文字なのに、ログイン時は半角英数字のみで4文字から12文字になってたりとかおかしなことになったりしないでしょうか?
「仕様書を見るから大丈夫だよ」という声も聞こえてきそうですが、定義はまとめておいたほうが変更しやすくて良いですよね。

そこで FormValidator::LazyWay です。検証ルールをフォームごとにせずに項目ごとにして怠けましょう。

設定

こんな感じで設定を書きます。

my $config = {
    rules => [ 'Email', 'String' ], # 使用するルール
    setting => {
        strict => {
            # Email は Email の書式でないとだめ
            email    => {
                rule => ['Email#email']
            },
            # password は4文字から12文字で半角英数字のみ
            password => {
                rule => [
                    {   'String#length' => {
                            'min' => '4',
                            'max' => '12'
                        }
                    },
                    'String#ascii'
                ]
            },
        }
    },
    lang   => 'ja',
    # バリデーションに引っかかったときのメッセージに出力するラベル名
    labels => {
        ja => {
            email    => 'メールアドレス',
            password => 'パスワード'
        }
    },
};

使用

そしてこんな風に使います。

use FormValidator::LazyWay;

# この $fv オブジェクトはサイト全体で使い回すと良いよ
my $fv =  FormValidator::LazyWay->new($config);

# $q は CGI とか Catalyst::Request とかのオブジェクト
my $result = $fv->check($q, {
    required => [qw/email password/],
});

フォームによって必須項目変えたければ変えればいいです。

たとえば、ブログ的なもので登録時は内容のみ必須でタイトルはオプショナル。

my $result = $fv->check($q, {
    required => [qw/body/],
    optional => [qw/title/]
});

そして検索時は両方オプショナル。

my $result = $fv->check($q, {
    optional => [qw/title body/],
});

各項目の検証ルールはすでに決まってるので、使うときはどの項目を検証すればいいかだけ考えればいいですね。

検証結果

検証結果には FormValidator::LazyWay::Result オブジェクトが返ってきます。

# $result が FormValidator::LazyWay::Result オブジェクト
my $result = $fv->check($q, {
    required => [qw/nickname email password/],
    optional => [qw/message/]
});

if ( $result->has_error ) {
    print Dumper $result->error_message;

    # 検証結果に応じてメッセージを自動生成してくれます。
    # このメッセージは設定で指定した label と
    # ルールモジュールで設定された内容を掛け合わせてます。
    # $VAR1 = {
    #   'email' => 'メールアドレスが空です。',
    #   'password' => 'パスワードには4文字以上12文字以下が使用できます。'
    # };
}
else {
    # OK!
    # 検証されたデータは $result->valid にハッシュとして持ってます。
    # 検証後はこの valid で取り出した値を使いましょう
    print Dumper $result->valid;
}

同じ項目で違うルールを指定したい

設定で level を変えてあげれば可能です。

ハッシュで書くと長いので YAML で書きますね。

config:
  setting:
    strict:
      email:
        rule:
          - Email#email
    loose:
      email:
        rule:
          - Email#email_loose

こんな風に strict と loose というのを用意してあげましょう。そして使うときはこんな感じ。

my $result = $fv->check( $q,
    {   required => [qw/email/],
        level => {
            email => 'loose'
        }
    }
);

email 項目が loose の方の設定で検証されるようになるよ。

項目をマージして検証

複数の入力項目から一つの値にして検証もできます。たとえば、年月日がそれぞれ別項目で、妥当な日時か検証したいとき。

config:
  setting:
    merge:
      date:
        format: "%04d-%02d-%02d"
        fields:
          - year
          - month
          - day
    strict:
      date:
        rule:
          - DateTime#date

year, month, day という項目を %04d-%02d-%02d というフォーマットで date 項目を作って、date 項目に対して妥当な日時かどうか検証します。

filter -> rule -> fix

FormValidator::LazyWay は、項目の検証の前後にフックを持っていて、検証前に項目の値の値と検証後の値の変更が可能です。

たとえば、ハイフンっぽい文字列をハイフンに統一したあとに、妥当な日時かどうか検証して、DateTime オブジェクトにして返すとか。

  setting:
    strict:
      date:
        filter:
          - Unify#hyphen
        rule:
          - DateTime#date
        fix:
          - DateTime#format:
              - '%Y-%m-%d'

問題無く検証できれば $result->valid->{date} が DateTime オブジェクトになって返ってくるよ。

そのほかにもメッセージのカスタマイズとか、param メソッド持ってないただのハッシュの検証とか色々機能があります。

日本語の POD もあるのでご参照いただければ幸いです

明日は……誰か書きませんか!

JPerl Advent Calendar hacker track の明日を作るのはあなたです!