― Web Technology and Life ―

FormValidator::Liteでパラメータまたぎの独自のエラーを追加する方法

2014-06-14
今更、FormValidator::Liteとか使うんですかとか言われそうですが、使うんです!FormValidator::Liteとか久しぶりに使うとなんか、忘れるしドキュメントみても特に書いていないので、メモっておきます

オレオレなコード

my $v = $c->validator;#FormValidator::Lite->new($c->req);

$v->check(
    id                   => ['REQUIRED','INT'],
    start_date           => ['REQUIRED','DATE'],
    end_date             => [           'DATE'],
);

if ($v->is_valid) {
    my $row = $c->db->single('campaign',{ id => $v->query->param('id') });

    if ($row->start_date < $v->query->param('start_date')) {
        #must_be_after_prevはREQUIREDやDATEなどに相当する文字列をいれる
        $v->set_error('start_date' => 'must_be_after_prev');
        $v->set_message('start_date.must_be_after_prev' => '開始日は直近のキャンペーンの開始日より後でなければなりません');
    }
};

サンプルのやりたいことがよくわからんという感じですが、元のコードをいじったら意味わからん感じになった。雰囲気で悟って下さい。。。

寸評その1 ~ checkの外にあるのがキモい件。

checkメソッドでバリデーションしているのに、その後でまたバリデーション追加するとかキモいですよね。

MATCHを使う

FormValidator::Liteだと、MATCHってバリデーションタイプがあるんですが、これをうまくゴニョゴニョすれば使える感じですね。

$v->check(
    id                              => ['REQUIRED','INT'],
    start_date                      => ['REQUIRED','DATE'],
    end_date                        => [           'DATE'],
    {custom => [qw/id start_date/]} => [[MATCH => sub {
        my ($id,$start_date) = @{$_};

        my $row = $c->db->single('campaign',{ id => $v->query->param('id') });

        reuturn ($row->start_date < $start_date) ? 1 : 0;
    }]]
);

まぁ、結局、set_messageを外だしするし、MATCHとか個人的になんかわかりづらい気がするんですよね。

HTML::Shakanを使う

HTML::Shakanって、いい感じのDSLのフォームバリデーターがあるのですが、ぼくは結構好きなんですが、重厚な感じでライトじゃないんですよねw これだと、custom_validationってところにいい感じに指定できて気持ち良い感じですね。

DSLの部分だけ抜粋します。

form 'blog_category' => (
    TextField(
        name        => 'tag',
        label       => 'Tag:',
        required    => 1,
        filters     => [qw/WhiteSpace +Hirobanex::Api::Form::FilterAlnumH/],
        constraints => [
            [ 'LENGTH', 1, 15 ],
        ],
        custom_validation => sub {
            my $form = shift;

            if ($form->is_valid && container('db')->single('blog_category',{ tag => $form->param('tag')})) {
                $form->set_error('tag' => 'failed');
                $form->set_message('tag.failed' => 'すでに登録されています');
            }
        }
    ),
);

寸評その2 ~ FormValidator::Liteを使う理由

UIエンジニアと分業とかだったりとか大きい仕事だと全然だと思うんですが、ひとりでコソコソやっているくらいだと、サーバーサイドでset_messageして、エラーメッセージを一括管理したいんですよね。

JSとか詳しくなくPerlしかほぼできないんで、ロジックなことはPerlで全部管理したいという感じ。

あと、まぁ、一般的にも、サーバーサイドが得意な人のシーズフェーズとか、モックフェーズな領域では活躍するんじゃないかな。

というわけで、サーバーサイドでバリデーションとエラーメッセージを管理するのもガラケーオワコン時代でも生き残っていくのではないかという次第です。

寸評その3 ~ それでもFormValidator::Liteが機能しないところ

クールURLということで、URLの文字列にうまくパラメータ的なものを仕込んだりしていくとFormValidator::Liteみたいなget/postのparamterを使う前提のモジュールは使い勝手悪いですよね。

あるいは、こういうURLにさらにパラメータつけちゃう場合とか。

get '/show/{category:\w+}/{article_id:[0-9]+}' => sub {
    my ($c,$args) = @_;

    my $row_article = $c->db->single('article',{ id => $args->{article_id} })
        or return $c->res_404();

    my $row_category = $c->db->single('category',{ name => $args->{category} })
        or return $c->res_404();
    .
    .
    .

};

こういう場合は、なんとなく一か所でまとめてバリデーションするくんをつくりたい気がしますが、今のところ、上記のようなDBたたいてバリデーションみたいなの済ますことしかしたことないので、それで済ませちゃっています。

$args, $c->reqをマージする

@toku_bussさんに指摘されたんですが、WAFによると思うんですが、ゴリゴリでパラメータと$args的なのをマージして呼び出していけば、一緒にバリデーションするの可能でした。勘違いしていますた。

sub show {
    my ($class, $c, $args) = @_;

    #my $v = $c->validator;
    my $v = FormValidator::Lite->new(+{ 
        %{$c->{args}}, 
        $c->req->parameters->flatten
    });

    my $row;
    $v->check(
        user_id            => ['REQUIRED',[MATCH => sub {
            my $id = $_;

            return ( $row = $c->db->single('user',{ id => $id }) ) ? 1 : 0;
        }]],
    );

    $v->is_error('user_id') and return $c->res_404();
    .
    .
    .
}

まぁ、とったデータを引き回そうとするとしたり、これくらいのしょぼいやつだと助長なんでアレですがね。もう少しパラメータがゴミゴミしてくると、バリデーションの分け方的に気持ちよくなれますね♪

あと、$c->validatorで気持ちよくなりたいぼくむけには、

ってことで、

package MyApp::Web;
use parent qw(Raptor Amon2::Web);

use FormValidator::Lite;
FormValidator::Lite->load_constraints(qw/Mouse Date/);
sub validator {
    my ($c) = @_;

    my $q =  ($c->{args})
        ? +{ %{$c->{args}}, $c->req->parameters->flatten}
        : $c->req
    ;

    my $validator = FormValidator::Lite->new($q);

    $validator->load_function_message('ja');

    return $validator;
}

という感じにすればよさげでした。。Amon2::Web::Dispatcher::RouterBoomの$c->{args}につっこまれているの、限定的ですが、いい感じですね!toku_bussさんあざっす!!

地味なはなし

セッションについて調べようと思ったら、ホントによくわかっていなかったので、コード読んでお腹いっぱいになったから、仕事に戻りほげほげしていたら、このような記事を書くに至りました。

バリデーションとかもわりと、地味ですが、地味な話は勉強中の身でこそかけるってもんですねw

Perl update_at : 2014-06-14T14:45:59
hirobanex.netの更新情報の取得
 RSSリーダーで購読する   
blog comments powered by Disqus