CakePHP3でカスタムバリデーション作ってみた。
2016/10/31 追記
カスタムバリデーションではないのですが、バリデーション関連の記事を追加してみました。
tsuralabo.hatenablog.com
チマチマ作ってるアレですが、管理画面の方に着手しています。
入力項目や登録するデータが出てくるので、どうしてもバリデーションが必要になってきます。
CakePHP3のバリデーションはCakePHP2までと少し変わっていまして、2パターンのバリデーションがあります。
1つ目はPOSTデータをチェックする際のバリデーション。
2つ目はsaveメソッド実行時のバリデーション。
ググるとわかりますが前者は「validationDefault」、後者は「buildRules」なんて言うらしいです。
コードで書くとこんな感じです。
<?php namespace App\Model\Table; use Cake\ORM\Table; use Cake\Validation\Validator; // validationDefault使う時に必要です use Cake\ORM\RulesChecker; // buildRules使う時に必要です class UsersTable extends Table { public function validationDefault(Validator $validator) { // POSTデータ受け取り時のバリデーション // 結果を返す return $validator; } public function buildRules(RulesChecker $rules) { // saveメソッド実行時のバリデーション // 結果を返す return $rules; } }
見たらわかると思いますが、CakePHP3ではTableにバリデーションを書きます。
上のソースだとUsersTableに書いてます。
バリデーションの書き方はCakePHP2の頃と似ているようで違います。
Validator型(でいいのかな?)の$validatorに対して、「このフィールド」に対して「こういうバリデーション」します、という書き方です。
例えばusernameというフィールドに対して入力必須のバリデーションをチェックしたい時は
<?php $validator->notEmpty('username', 'ユーザ名が入力されていません。');
こんな感じです。
さらに4文字以上、8文字以内というバリデーションをチェックしたい時は
<?php $validator ->notEmpty('userid', 'ユーザIDが入力されていません。') ->lengthBetween('username', [4, 8], 'ユーザIDは4文字以上、8文字以内。');
このようになります。
他のフィールドもチェックしたい時はどんどん繋げていけばいいのですが、ソースが見難くなるかもしれません。
そんな時は
<?php $validator ->notEmpty('username', 'ユーザ名が入力されていません。') ->lengthBetween('username', [4, 8], 'ログインIDは4文字以上、8文字以内。'); $validator ->notEmpty('password', 'パスワードが入力されていません。') ->lengthBetween('password', [8, 32], 'パスワードは8文字以上、32文字以内。');
と分けて書いても大丈夫です。
個人的にはこちらの書き方の方が好きです。
理由は何の項目に対して、どんなバリデーションをチェックしているか見やすいからです(個人的な意見です)
notEmptyが何とか、lengthBetweenって何? とかはグーグル先生に聞いてみて下さい。
さて。
今回はユーザIDに対して半角英数字のみというバリデーションをチェックすることにします。
CakePHP2の頃からある「alphaNumeric」を使えばよさそうなのですが、これ実は日本語を通してしまうんです。
なので少なくとも日本で展開するWebサービスだと使いものになりません(多分)
CakePHP3本体のValidationを書き換えればいけちゃうと思うんですが、あまり本体には手を入れたくない。
こんな時、CakePHP2だったらAppModelにサラっと書いて、それを呼び出せばよかったのですがCakePHP3にはありません。
ですので、本家のマニュアルを見ながらそれっぽいものを作ってみました。
それがやっと辿り着いた、カスタムバリデーションです。
本家の書き方を踏襲して、src\Model配下にValidationというディレクトリをとりあえず作ります。
作ったValidationディレクトリにCustomValidation.phpを作成します。
中身はこんな感じです。
ついでに半角英数字のバリデーション用のfunctionも書いてしまいます。
ただの正規表現なんですけどね。
<?php namespace App\Model\Validation; use Cake\Validation\Validation; /** * カスタムバリデーションサンプル */ class CustomValidation extends Validation { /** * 半角英数字バリデーション * @param type $check * @return type */ public static function alphaNumericCustom($check) { return (bool) preg_match('/^[a-zA-Z0-9]+$/', $check); } }
名前はもちろん、CustomValidationじゃなくて構いません。
RururuValidationだろうが、SpecialValidationだろうが何でもOKです。
ただ、注意する必要があるのは以下の2点。
その1 作成するfunctionは「static」にすること
その2 作成したfunctionの戻り値はbool型にすること
この2点です。
試しにstatic取ってみると、staticで書けとエラーが出ます。
これさえ守ればあとはただの正規表現的な何かでしかありません。
とりあえずカスタムバリデーションとかいう偉そうな名前の正規表現ができたので、先程のバリデーションに組み込みます。
<?php /** * POST処理時のバリデーション * @param Validator $validator * @return Validator */ public function validationDefault(Validator $validator) { // カスタムバリデーション設定 // 書き方は「$validator->provider('プロバイダのキー', 'カスタムバリデーションのパス');」です。 $validator->provider('ProviderKey', 'App\Model\Validation\CustomValidation'); $validator ->notEmpty('username', 'ユーザ名が入力されていません。') // ここからカスタムバリデーション ->add('username', 'ruleName', [ 'rule' => ['alphaNumericCustom'], 'provider' => 'ProviderKey', // カスタムバリデーション設定で書いたプロバイダのキーを入れます。 'message' => 'ログインIDは半角英数字で入力してください。']) // ここまでカスタムバリデーション ->lengthBetween('username', [4, 8], 'ログインIDは4文字以上、8文字以内。'); // 結果を返す return $validator; }
※2016/07/11にここから修正しました ----------
カスタムバリデーション設定と書いている部分は、CakePHP3的に言うとプロバイダ設定です。
呼び出しというか、バリデータに対して「このプロバイダ使いますよ」追加してるわけです。
「add」メソッドでカスタムバリデーションに作ったfunctionを追加すると、無事にカスタムバリデーションが使えるようになります。
ruleNameと書いてある箇所はバリデーションのルール名です。適当に入力しておいて下さい(ダメかもしれませんが)
試したみた感じ、プロバイダのキーは何でもOKでした。
上の例だと「ProviderKey」と書いてますが、RururuでもSpecialでも何でも大丈夫です。
※ここまで修正しました-----------------------
バリデーションチェックでエラーが発生した場合、設定したメッセージが返ってきます。
それを呼び元でごにょごにょしてViewにメッセージ表示すればいいんじゃないかなと思います。
一応、思い出したかのようにController側からの呼び出し方も書いておきます。
<?php /** * ユーザ追加functionということにします */ public function add() { // POSTデータを渡す $user = $this->Users->newEntity($this->request->data); if (!$user->errors()) { // バリデーションエラーがなかった場合 } else { // バリデーションエラーの場合 } }
エラー時はerrorsという中にエラーメッセージが入ってるので、Flashなどでメッセージを表示するといいです。
エラーメッセージを1つずつまとめて表示したい!という場合は、イケてない書き方ですが
<?php /** * ユーザ追加functionということにします */ public function add() { // POSTデータを渡す $user = $this->Users->newEntity($this->request->data); if (!$user->errors()) { // バリデーションエラーがなかった場合 } else { // バリデーションエラーの場合 foreach ($user->errors() as $error) { foreach ($error as $message) { pr($message); } } } }
$message辺りを配列にブチ込んでView側で出力しながら改行コード入れると、それっぽくなります。
もう少しいい書き方できればいいんですが、模索中ということで。
ちなみにbuildRulesの方も書き方は同じです。
多分。