2012年7月31日火曜日

独自のバリデーションで検証済みの値を変更する(FuelPHP)

独自のバリデーションを追加するやり方は以前にも書いたんですが
検証済みの値 $validation->validated() で取得する値を変更するやり方。

やりたかったのは日付チェック用のルールを作っておいて検証が成功した場合の戻り値を統一したい。
入力例:2012/7/31, 2012-07-31, 2012年7月31日
検証済み:2012-07-31

myvalidation.php
public static function _validation_valid_date($val) {
    // 検証を記述(省略)

    // 検証後
    // return true
    return date('Y-m-d', strtotime($val));
}

こうしておくと
// date = '2012年7月31日';

$val = Validation::forge();
$val->add_callable('myvalidation');

$val->add('date', '日付')
    ->add_rule('valid_date');

if ($val->run())
{
    echo $val->input('date');        // 結果:2012年7月31日
    echo $val->validated('date');    // 結果:2012-07-31
}


こんなやり方でいいのかな。。。
動いたからいいか

2012年7月30日月曜日

validationで日付のチェック

FuelPHP の Validation で日付のチェック

正規表現で値を分解して checkdate 関数でチェックやっただけ。
public static function _validation_valid_date($val)
{
    if (!$val)
    {
        return true;
    }
    
    $parts = array();
    // 形式はyyyy-mm-dd
    if (!preg_match('/^([0-9]{4})[\-\/\.](0?[0-9]|1[0-2])[\-\/\.]([0-2]?[0-9]|3[01])$/', $val, $parts))
    {
        return false;
    }
        
    if (checkdate($parts[2], $parts[3], $parts[1]) === true)
    {
        return true;
    }
    else
    {
        return false;
    }
}

日付の比較チェック

引数を1つ追加して比較用のフィールド名を指定
public static function _validation_compare_date($val, $field)
{
    if (!$val)
    {
        return true;
    }
    if (!Input::post($field))
    {
        return true;
    }
        
    $tmp_val_st = strtotime(Input::post($field));
    $tmp_val_ed = strtotime($val);
        
    if ($tmp_val_st > $tmp_val_ed)
    {
        return false;
    }
        
    return true;
}


もっときれいなソースをかけるようになりたい・・・



いままで正規表現するとき ereg 関数を使ってたけどこれは PHP5.3 から非推奨となってました。ereg 関数を使っている場合は preg_match 関数に置き換えないと・・・知らんかった。
PHP: ereg - Manual
PHP: preg_match - Manual




2012年7月27日金曜日

show_errorsの表示をちょっと変えてHTML5風に

題名はかなり大げさなこと書いてますが、jQueryのプラグインを久しぶりに作ってみようと思っていま勉強中のFuelPHPと合わせて適当にいじってみました。

※下に書いてあることは別にプラグインを作らなくてもCSSだけでどうにでもなります・・・ただプラグインを作ってみたかっただけです。


HTML5を使って input に required 属性を付けると

こんなかんじで格好いいです。
でもこれは HTML5 を使ってるんで validation したあとのエラーメッセージとか至って普通に表示されます。(Email アドレスの記述が間違っている場合のエラー)


これを HTML5 風に変えてみよう・・・
(いろいろめんどくさくなってきたんで途中でやめましたが・・・)

簡単にやったこと

  • Formクラスを使って input を生成
  • Validationクラスをちょっとだけ修正
  • ConfigのValidationにopen_errorを追加
  • jQueryプラグイン作って埋め込む

まず、input の生成。これは普通。(required属性はつけません)
<?php echo Form::input('name', '名前'); ?>

次に、fuel/core/classes/validation.php と fuel/core/classes/validation.error.php を修正します。

fuel/core/classes/validation.php 行560
// $output .= $options['open_error'].$e->get_message().$options['close_error'];
$output .= $e->_replace_tags($options['open_error']).$e->get_message().$options['close_error'];

open_error のタグをエラーメッセージと同じように置換するようにメソッドを呼び出します。が、_replace_tagsは protected なんで public に変更しました。

fuel/core/classes/validation/error.php 行113
// protected function _replace_tags($msg)
public function _replace_tags($msg)

これでOK。
つぎに、fuel/app/config/config.php の validation に open_error を以下のように追加。
'validation' = array(
    'open_error' => '<li class="error_:field">',
);

上の :field の部分は validation や input で指定している id になります。

これであとはjQueryのプラグインを埋め込んで実装するだけです。
jquery.validation.js を作って中身は以下
(function($){
    function Validation() {
        this._defaults = {
            form_identifier: 'form_',
            error_identifier: 'error_',
        };
        this._target = null;
        
        this.errors = {};
    }
    
    $.extend(Validation.prototype, {
        _attach: function(element) {
            this._target = element;
            this._target.css('display', 'none');
            
            this._errors = this._get_errors();
            for (i = 0; i < this._errors.length; i++) {
                this._create_message(this._errors[i]);
            }
        },
        _get_errors: function() {
            errors = [];
            
            $("li", this._target).each(function(idx, val) {
                error = {};
                error['id'] = $(val).attr('id')
                                .replace($.validation._get('error_identifier'), '');
                error['text'] = $(val).text();
                
                errors.push(error);
            });
            
            return errors;
        },
        _create_message: function(error) {
            e = $('<div>')
                    .addClass('validation_message')
                    .text(error.text)
                    .css('display', 'none');
            
            t = $('#'+this._get('form_identifier')+error.id);
            t.after(e);
            
            e.css({
                'display': 'inline',
                'position': 'absolute',
                'top': t.position().top + (t.outerHeight() - e.outerHeight()),
                'left': t.position().left + t.outerWidth() + 10,
            });
        },
        _get: function(key) {
            return this._defaults[key];
        },
    });
    
    $.fn.validation = function(options) {
        $.extend($.validation._defaults, options);
        $.validation._attach(this);
        return this;
    }
    
    $.validation= new Validation();
    window.VALIDATION = $;
    
})(jQuery);

コメントも何もないですがshow_errors()で表示している内容をとってきてそれぞれのinputの横に出してるだけです。(汚いソースですみません)

使うときはこんなかんじ。
<div class="error">
    <?php echo $validation->show_errors(); ?>
</div>

<?php echo Asset::js('jquery-1.7.2.min.js') ?>
<?php echo Asset::js('jquery.validation.js') ?>
<script type="text/javascript">
    $(document).ready(function() {
        $('div.error').validation();
    });
<script>


こんなかんじで表示されます。


最初にも言った通り、べつにプラグインの必要はないです。
それぞれの項目の場所で
<?php echo $valication->error('name'); ?>

とかしてこれにスタイルをつければ同じことできます・・・



2012年7月26日木曜日

チェックボックスのバリデーションや値の受け渡しで悩む(FuelPHP)

FuelPHPのFormクラスを使ってチェックボックスを使う際にちょっとハマったことを。

まず、バリデーションについてですが自前で実装しました。自前といってもいいサンプルがあったんでそれを真似してます。

FuelPHPでcheckboxのバリデーション
http://btt.hatenablog.com/entry/2012/06/14/143043


次に値の受け渡しです。

普通の input とかの場合だと
<?php echo Form::input('name', Input::post('name', isset($user) ? $user->name : '')); ?>

バリデーションでエラーを戻した場合は Input::post で入力された値を取得、post されていない場合はモデルから取得した値、何もなかったら空白。

チェックボックスの場合、リストを配列から回しながら表示していくと思います。
for ($groups as $group) {
    echo Form::checkbox('groups[]', $group->id, array('id' => 'group_'.$group->id));
    echo Form::label($group->name, 'group_'.$group->id);
}

ただ表示するだけだとこんなかんじになるのかなと・・・

これに input と同じような仕組みを付け加えると
for ($groups as $group) {
    echo Form::checkbox('groups[]', $group->id,
                            in_array($group->id, Input::post('groups', array())) ?
                            array('id' => 'group_'.$group->id, 'checked' => 'checked') :
                            array_key_exists($group->id, isset($user) ? $user->groups : array()) ?
                            array('id' => 'group_'.$group->id, 'checked' => 'checked') :
                            array('id' => 'group_'.$group->id)
                        );
    echo Form::label($group->name, 'group_'.$group->id);
}

なんか大変なことになってしまった・・・

checkbox を Input::post で受け渡しすると checkbox の value が配列の値として入ってくるけどモデルから取得した値は、キーになってる。
(中身を展開すればもちろん値としてもあるわけですが・・・)

考えてると結果こうなってしまいました。
もっとスマートな方法はないものかと・・・



2012年7月25日水曜日

モデル間のリレショーンをためしてみた(FuelPHP)

Orm(Object Relation Mapping)の利点はリレーションを意識したモデル操作ができる。
と本に書いてたんで試してみました。

試してみたのはユーザとグループを関連付けるだけ。
必要なテーブルはユーザ(users)とグループ(groups)、この2つを関連付ける中間テーブル(users_groups)の3つです。

まずは oil コマンドでモデルを作ります。
$ oil g model users name:varchar[50] login_id:varchar[50] password:string
$ oil g model groups name:varchar[50]
$ oil refine migrate

fuel/app/classes/model に user.php と group.php が作られ、
oil refine migrate を実行するとテーブルも作られると思います。

次に中間テーブル(users_groups)
CREATE users_groups (
    user_id int(11) NOT NULL,
    group_id int(11) NOT NULL,
    PRIMARY KEY (user_id, group_id)
);

これで準備はOK。

やりたいことはユーザを登録するときに合わせてグループの関連付けも登録、削除するときは関連付けも一緒に削除。

まず、モデルファイル(user.php)に以下を追加します。
protected static $_many_many = array(
    'groups' => array(
        'key_from' => 'id',
        'key_through_form' => 'user_id',
        'table_through' => 'users_groups',
        'key_through_to' => 'group_id',
        'model_to' => 'Model_Group',
        'key_to' => 'id',
        'cascade_save' => true,
        'cascade_delete' => true,
    ),
);

こんなかんじでリレーションの設定を行います。
項目の詳細はマニュアルを見るのが一番かなと。(自分もまだよくわかってないし)
http://docs.fuelphp.com/packages/orm/relations/many_many.html

次に実際にコントローラから登録をする処理です。
public function action_create()
{
    // グループ情報を取得
    $groups = Model_Group::find()->get();

    if (Input::method() == 'POST')
    {
        // Authインスタンス
        $auth = Auth::instance();

        // ユーザ
        $user = Model_User::forge();
        $user->name = Input::post('name');
        $user->login_id = Input::post('login_id');
        $user->password = $auth->hash_password(Input::post('password'));

        // 関連付け
        if (Input::post('group_id'))
        {
            for (Input::post('group_id') as $group_id)
            {
                $group = Model_Group::find($group_id);
                if ($group)
                {
                    $user->groups[] = $group;
                }
            }
        }

        // 登録
        $user->save();
    }
}

ざっくり書きましたがこんなかんじになります。
グループの関連は複数を相当して書いてみました。
これを動かすと users と users_groups のテーブルに値が書き込まれるはずです。

またこれでユーザを削除する場合も $user->delete() で users_groups も削除されますし、データを取得する場合も $user->find($id) だけで users_groups の情報も取得してきます。

いろいろ関連ついてくると複雑になりそうなんで簡単ものから徐々に。

※書籍には「中間テーブルに対応するモデルを作成することで、_many_manyを使わず_has_manyを使う」と書いていたんですが・・・
なんかうまくいかなかったです。時間見つけたまたトライしてみます。



2012年7月23日月曜日

メモ:Sassのmixinでオプション引数をつける

mixin で関数作ってたんですけどオプション引数のやり方。

@mixin border($top, $right, $bottom, $left, $color: #666) {
    border-style: solid;
    border-color: $color;
    border-width: $top $right $botom $left;
}


上のように $color: #666 と記述します。
必死に $color = #666 と書いてました。。。(もちろんコンパイルエラーです)




SimpleAuthを参考にいじってみました(FuelPHP)

FuelPHPを使ってログイン機能を実装するのに SimpleAuth で実装しようとしてたんですが SimpleAuth の username, email, password は変更できないという指摘を受けましたんで、自分なり SimpleAuth を参考に Auth のドライバを作ってみました。




コードは恥ずかしいので載せませんがやったこと箇条書き。

  1. fuel/packages/auth/classes/auth/login/simpleauth.php をコピー(名前は適当に easyauth.php としました)
  2. easyauth.php を開き、クラス名を Auth_Login_EasyAuth に変更
  3. ここで一度動かしてみようと思ったらエラーで動きませんでした。
    Class 'Auth_Login_EasyAuth' not found
  4. この Auth_Login_EasyAuth を認識させるために bootstrap.php にも記述が必要みたいなので fuel/packages/auth/classes/auth/bootstrap.php に追記します。
    'Auth\\Auth_Login_EasyAuth' => __DIR__.'/classes/auth/login/easyauth.php',
    
  5. これでもう一回動かしてみると、not found のエラーが消えました
  6. 次に fuel/packages/auth/config/auth.php と simpleauth.php を fuel/app/config にコピーして simpleauth.php を easyauth.php に名前を変更しておきます
  7. あとは fuel/packages/auth/classes/login/easyauth.php を変更していきました

ざっくりです。
躓いたところは bootstrap.php に追記するっていうのくらいですかね。
あとは auth が抽象メソッドを持ってるのでそれさえ消さずに書き直すと動作すると思います。