読者です 読者をやめる 読者になる 読者になる

dari88's diary

これから趣味にするプログラミング/PHP/javascript/kohana/CMS/web design/

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 ってよくできていますね、関心します。