kohanaのテスト6・・・Formの入力をValidationで検証する
テスト5まではフォームからのユーザーの入力を検証していませんでした。今日はkohana の Validation を使って検証する実習です。入力フォームも kohana の Form を使います。元ネタのサンプルコードは kohana ユーザーガイドのものです。このサンプルコードは A Complete Example と名打っているんですけど、ウソです。バグはあるし、未完成だしで、動かすまで結構時間が掛かりました。しかし、すんなり動かないからこそ勉強になるんですよね。
では、今日のサンプルコードとその説明です。
データベースの準備
最初に phpMyAdmin を使って、データベース kohana にテーブルを作ります。次の SQL を利用して下さい。ユーザーガイドはこんなに親切じゃないです・・・(^^;;;
# This is a fix for InnoDB in MySQL >= 4.1.x # It "suspends judgement" for fkey relationships until are tables are set. SET FOREIGN_KEY_CHECKS = 0; #----------------------------------------------------------------------------- #-- test2 #----------------------------------------------------------------------------- DROP TABLE IF EXISTS `test6`; CREATE TABLE `test6` ( `username` VARCHAR(20) NOT NULL, `password` VARCHAR(20), `confirm` VARCHAR(20), `use_ssl` VARCHAR(5), PRIMARY KEY (`username`) ) ENGINE=InnoDB CHARACTER SET 'utf8'; INSERT INTO test6 VALUES ('root', 'root1234', 'root1234', 'no'); INSERT INTO test6 VALUES ('admin', 'admin1234', 'admin1234', 'no'); INSERT INTO test6 VALUES ('user1', 'user1234', 'user1234', 'yes');
コントローラー
・application/classes/controller/test6.php
<?php defined('SYSPATH') OR die('No direct access allowed.'); class Controller_Test6 extends Controller { // public function action_register() // 名前を変更した・・(1) public function action_index() { $user = Model::factory('test6'); // モデルを指定・・(2) if (isset($_POST['username'])) { // POSTされた状態か確認を追加・・(3) $post = Validation::factory($_POST) ->rule('username', 'not_empty') ->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD')) // ユーザー評価関数を作成した・・(4) ->rule('username', array($user, 'unique_username')) ->rule('password', 'not_empty') ->rule('password', 'min_length', array(':value', 6)) ->rule('confirm', 'matches', array(':validation', ':field', 'password')) ->rule('use_ssl', 'not_empty') ->rule('use_ssl', 'in_array', array(':value', array('yes', 'no'))); // $postをデータの配列に変換する行を追加・・(5) $post_array = $post->data(); if ($post->check()) { // $user->register($post); // $postはデータの配列に変換する必要がある $user->register($post_array); // 認証されたらリダイレクトすること・・(6) $this->request->redirect('test5'); } // エラーメッセージを渡す・・(7) $errors = $post->errors('test6'); } // オリジナルは最後の')'の位置が間違っていた・・(8) // $postを配列に変換してから渡す必要がある $this->response->body(View::factory('test6') ->bind('post', $post_array) ->bind('errors', $errors)); } } ?>
<説明>
・(1)で function名を変えています。元の名前 action_register ですと、
localhost/test6/register
にアクセスしなければなりません。
・(2)でモデル test6 のインスタンスを作っていますが、テスト5の時と作法が違います。PHP の new を使わず、kohana の Model::factory() を使っています。何のご利益があるんだろう?・・と感じますが、きっとご利益があるんでしょう。
・(3)で POST 後か否かを条件分岐させています。
・(4)はマニュアルの説明不足で理解するまで時間を食いました。ここではユーザー名がユニークか否かを検証しています。モデルに unique_username というメソッドを追加してやる必要があります。
しかし、kohana はどうやって unique_username() を見付けるのでしょうか?モデルのインスタンスを作る時に Model::factory() を使ったことと関係があるような気がします。
・(5)ですが、今回のモデルやビューに $post をそのまま渡しても処理できていないので、事前に配列に変換してやります。
・(6)認証を通ったらリダイレクトするのがお約束ということです。
・(7)この記述で、application/messages/test6.php をロードして、入力エラーに対応するエラーメッセージを $errors に配列として書き込んでくれます。
・(8)オリジナルのサンプルコードにバグがありました。bind は View のメソッドです。
モデル
・application/classes/model/test6.php
<?php defined('SYSPATH') OR die('No direct access allowed.'); class Model_Test6 extends Model { public function register($array) { // テーブル名の指定を追加した・・(1) $id = DB::insert(array_keys($array)) ->values($array) ->table('test6') ->execute(); // クッキーの検討は別途・・(2) // cookie::set('user', $id) return $id; } // ユーザーIDのユニーク性を検査する関数を追加・・(3) public function unique_username($username) { $users = DB::SELECT() ->from('test6') ->select('username') ->execute(); foreach ($users as $user) { if ($user['username'] == $username) { return false; } } return true; } } ?>
<説明>
・(1)テーブル名の指定が抜けていたので追加しました。
・(2)これだけじゃ cookie は動きませんので、別の機会にということで。
・(3)ユーザーIDのユニーク性を検証するメソッドを書き加えました。
ビュー
・application/views/test6.php
<?php echo Form::open() ?> <h1>ユーザー登録画面</h1> <?php if ($errors): ?> <p class="message">入力値はよくチェックして下さい。</p> <ul class="errors"> <?php foreach ($errors as $message): ?> <li><?php echo $message ?></li> <?php endforeach ?> <?php endif ?> <dl> <dt><?php echo Form::label('username', 'ユーザー名') ?></dt> <dd><?php echo Form::input('username', // POST前の表示対策を追加・・(1) isset($post['username']) ? $post['username'] : "") ?></dd> <dt><?php echo Form::label('password', 'パスワード') ?></dt> <dd><?php echo Form::password('password') ?></dd> <dd class="help">パスワードは6文字以上必要です</dd> <dt><?php echo Form::label('confirm', 'パスワードの確認') ?></dt> <dd><?php echo Form::password('confirm') ?></dd> <dt><?php echo Form::label('use_ssl', '安全なSSL通信を使いますか?') ?></dt> <dd><?php echo Form::select('use_ssl', array('yes' => '常時SSL通信', 'no' => '必要な時だけSSL通信'), // POST前の表示対策を追加・・(1) isset($post['use_ssl']) ? $post['use_ssl'] : "") ?></dd> <dd class="help">パスワードなどを送信する時はSSL通信を使います。</dd> </dl> <?php echo Form::submit(NULL, '登録') ?> <?php echo Form::close() ?>
<説明>
・Form ヘルパーを使うことで、記述が簡単になっていると思います。
メッセージ
・application/messages/test6.php
<?php defined('SYSPATH') or die('No direct script access.'); // kohana/system/messages/validation.phpからコピー return array( 'alpha' => ':field must contain only letters', 'alpha_dash' => ':field must contain only numbers, letters and dashes', 'alpha_numeric' => ':field must contain only letters and numbers', 'color' => ':field must be a color', 'credit_card' => ':field must be a credit card number', 'date' => ':field must be a date', 'decimal' => ':field must be a decimal with :param2 places', 'digit' => ':field must be a digit', 'email' => ':field must be a email address', 'email_domain' => ':field must contain a valid email domain', 'equals' => ':field must equal :param2', 'exact_length' => ':field must be exactly :param2 characters long', 'in_array' => ':field must be one of the available options', 'ip' => ':field must be an ip address', 'matches' => ':field が間違っています', 'min_length' => ':field は :param2 文字以上にして下さい', 'max_length' => ':field must not exceed :param2 characters long', 'not_empty' => ':field が空ではいけません', 'numeric' => ':field must be numeric', 'phone' => ':field must be a phone number', 'range' => ':field must be within the range of :param2 to :param3', 'regex' => ':field が規定の形式になっていません', 'url' => ':field must be a url', 'unique_username' => 'そのユーザー名は既に使われています', );
<説明>
・kohana/system/messages/validation.phpからコピーしてきたものに、必要な部分だけ日本語に変えています。英語のエラーメッセージで kohana フォルダ以下を grep してこのファイルを発見しました。
・最後に 'unique_username' を追加していますが、モデルのメソッド名です。こう書けるということも、実は試行錯誤して発見しています。
まとめ
今回のサンプルコードではいろいろと勉強になりました。CMS ってよくできていますね、関心します。