早くもやってしまった;;

12月 23, 2007 · Posted in · 1 Comment 

デュアリス購入して半年。6ヶ月点検に行ってきました。
ま、点検自体は特になんてこと無かったのだけど、その前に事件が(涙)

それは、11月も末日。自分の誕生日あたりのこと。
住宅ローンの契約銀行がちょっと遠くにあるため記帳するのに車で10分程度いつも移動する。月末ローン支払い前の残高か確認のためだ。
11月の末日も例によっていったわけだが、その帰り道事件は起こった!!

ちょいと近道しようと裏道を通っていたときのこと。
ここを右折すれば望みの場所にショートカットできるぜ!!というところがあった
が、そこには左折オンリー表示が;;
「なにおえー」と言いつつ左折したときである。

「ドンッ! ズズズズズッーーー」

という音が。
ドアミラーを見るとなんか引きずっている!!

車の哀しき傷痕

すぐに車を止めて確認すると
左後輪部分をカバーしている樹脂カバーが外れている!!
そしてよく見ると、左後輪の前の部分の塗装がぱっくり割れている!!(涙)
あと、ちょっとへこんでるし・・・

写真ではよく分かりませんが、塗装面がぱっくり割れていて下地が見えています;;
潮風吹き荒れるうちの方ではさびが心配。

ちょっとした不注意だったけど、やってしまいましたよ!!
前回のプジョーを乗り始めて約10年。これほど激しくぶつかったのは初めてだわ(涙)

さて、ここで6ヶ月点検に話は戻る。
点検と丁度時期があったので今回の修理見積をしてもらった

ところ・・・

なんと・・・

203,219円!!!

ちょwまってw

早速担当営業さんに相談。
営業さん実は知り合いからの紹介で近くのディーラーではなく、横浜方面の人。
ディーラーとはあまり仲良くなっていないのでここは営業さんに相談だー

営業さんにも携帯で写真を送ってみたところ
どうもディーラーの見積はリヤドアすべて交換という見積だったようだ。そらかかるわな。
板金だけで済むのかと思ってたんだけど・・・

営業さんも板金で済むんじゃないかと・・・と言っているので
来週営業さんが見に来てくれることになった(営業さん元は整備士さん)

さー、どうなるべいか


柱の傷は思い切れないw – 4歳6ヶ月5日

12月 18, 2007 · Posted in 子育て, 木組みの家 · Comment 

なんか CakePHPのことばっか書いてたので違うことを・・・w

うちの家には1階の今の真ん中に 16.5cm角の大黒柱が1本2階まで通っている。
邪魔かもしれないとは思ったけどこれが結構邪魔になってなかったりする。

大黒柱には娘の身長を測った跡をつけている。基本ですな。
引っ越してきてすぐに1歳になったので1歳から記録されている。
しかし思い切りよく傷を付けられず、ちょろりとシャープペンで強めに書きつつ傷がちょっとはいっている状態。がりっと跡を付けられる程思い切れないのである。

その娘は先日丁度4歳半。
成長が早い今の時期は半年ごとに記録をしていて、早速測ってみた。
丁度 1m。4歳では標準かちょっと小さいくらいなのかな?
何となく大台に乗った感じでうれしいですね。

さて、その翌日柱の前を通り過ぎる娘を見て違和感が・・・
「どう見ても、昨日付けた跡よりも大きくないか!?」
柱の前には絨毯がひいてあるのでそのせいかと思い、ちょっとどけてみても一度測ってみたら、やっぱり気のせいじゃない。

1cm 大きいw

そういや昔自分の背をこまめに測ってた時(1番前か2番目か?と言う感じで背が低かったので気にしてよく測ってたのよ)朝の方が大きかったかもと思いだしたけど、 1cmも違ってたっけ?
とあたまに?を残しつつその日は終了。

やっぱり気になるので、夜にまた測ってみたら今度は 1m!

やっぱり朝は 1cm 伸びてたのか・・・

結構朝と夜とで違うのね。
とりあえず、公式的には娘の身長は現時点で 1m ということにしています。それまでも夜に測ってたからね。


CakePHP 認証機能

12月 9, 2007 · Posted in CakePHP, PHP · 5 Comments 

ログイン認証出来る機能がほしかったのだが標準で実装されているACLって認証機能じゃなかったのね orz
データベースなどのユーザーテーブル上のID/PWで認証できる機能を探していたところ、いろいろとモジュールが見つかり以下が参考になった。

とはいえ、個人で実装するにはいろいろと足りないところもあったりしたので、いいところを真似しつつ自作してみた。
それなりのレベルまで作れた感じな為公開してみることにした。不具合点などはご指摘下さい。
またご利用に際しては自己責任でお願いします。

使い方

コンポーネントとして動作。controllers/components にauth.phpとして配置。

まずはコンポーネントを利用するためにコントローラのはじめに以下のように設定

  var $components = array('Auth');

また、認証が必要なアクションを設定。
beforeFilter() メソッド中に認証が必要になるアクションを定義する方法を採用。

    function beforeFilter() {
        // 認証設定
        $this->Auth->requireAuth('userMenu', 'editUser');
        
        // 二重登録防止
        $this->Security->requireAuth('editUser');
    }

Securityコンポーネントと同様の方法で、認証が必要なアクションについて、requireAuth()の中にカンマ区切りでアクション名を羅列する方法としました。
検索して見つけたもろもろのモジュールでは、すべてに対して認証をしていたり、アクションの実行時に呼び出しを行ったりと面倒な方法をしているのが多かったのと、Securityコンポーネントと同じ方法で対応した方がわかりやすかったという理由で、こうしてみた。
Security->requireAuth()で二重投稿防止などをしている場合はその宣言より前に行う必要があります。

課題としては、無制限に認証をかける場合は allとか指定すると認証とか出来るといいのかもしれません。ま、今のところ必要ないから作ってないけどw

認証されているかをチェックする場合は、

  $this->Auth->checkAuth();

にてチェック。

ログアウトをする場合は、

  $this->Auth->logout();

とする感じで。

また、認証は ‘member’テーブルの ‘userid’ / ‘password’ を利用して行い、該当するユーザーが見つかった場合は ‘member’テーブル上のそのデータを認証ユーザーデータとして保存します。
実際は ‘Member’モデルで find()したユーザーデータが配列として保存されます。それらの値を取得する場合は、

  $value = $this->Auth->getAuthData('フィールド名');

member_name というフィールドの値を取りたい時は getAuthData(‘member_name’) で取得。

コンポーネント側の設定

$authFormName:
ま、authFormNameという命名が正しいかどうかは微妙です。
ログインフォームで $html->input(‘Login/userid’) などのように指定する際のモデル名(左では’Login’)を指定。

$authModelName:
認証で指定するモデル(テーブル)名。

$authLoginIdField:
ユーザーIDが格納されているフィールド名。 $authModelNameテーブルの $authLoginIdFieldがIDということになります。

$authPasswordField:
パスワードが格納されているフィールド名。 $authModelNameテーブルの $$authPasswordFieldがパスワードということになります。

$authLifeTime:
$authLifeTimeUnit:
上が認証有効期間、下がその単位。60分の場合は
$authLifeTime = 60;
$authLifeTimeUnit = ‘minutes’;
となる。strtotime() で比較するのでそこで認識できる単位を指定する。

$loginFormUrl:
ログインフォームURL。ログインが必要な場合はこのURLにリダイレクトされる。

$sessionName:
認証情報を保存するためのセッション名。ま、適当に。

ログインエラーとしては ID不正、パスワード不正、認証有効期間オーバーを用意しており、それらが
$this->Auth->getError() で取得出来るので、取得した内容をログインエラーとして表示できるんだけど、この辺がもうちょっとうまいこと実現できないかなーと思っているところ。
現状、たとえば loginForm() というアクションを用意して

    function loginForm() {
        $this->data[$this->Auth->authFormName] = $this->Auth->getLoginFormData();
        $this->set('errMsgs', $this->Auth->getError());
    }

のように errMsgsにエラー情報を渡してログイン画面を表示する必要があり、一つアクションに命令を各必要がある。
できればここは、書かずに処理したいんですよね。コンポーネント側だけで解決出来るといいかなーと。

書くべきところはこんな感じかしら?

ソース[auth.php]


class AuthComponent extends Object
{
    var $controller = true;
    var $components = array('RequestHandler', 'Session');
    
    /** 認証が必要なアクション名を格納する配列 */
    var $requireAuth = array();
    
    /** ログインフォームのフォーム名 */
    var $authFormName = 'Login';
    
    
    /** 認証で利用するモデル */
    var $authModelName = 'Member';
    var $authModel = null;
    var $authMember = null; 
    
    /** 認証モデルのログインID フィールド名 */
    var $authLoginIdField = 'login_id';

    /** 認証モデルのパスワード フィールド名 */
    var $authPasswordField = 'password';
    
    /** 認証有効期間 */
    var $authLifeTime = 60;
    /** 認証有効期間の単位 */
    var $authLifeTimeUnit = 'minutes';
    /** 認証有効時間(期間から算出) */
    var $authExpire;
    
    /** ログインフォームへのURL */
    var $loginFormUrl = '/members/loginForm';
    
    /** ログイン完了後にリダイレクトするURL */
    var $authLoginBackUrl = '';
    
    /** 認証で利用するセッション名 */
    var $sessionName = 'dragonquest';
    /**
     * セッションに引き渡し認証比較をするためのデータ
     *      array('Member' => array(),  ---> ログインしたユーザーの情報、authModelからfindして取得
     *            'Login'  => array(),  ---> ログインフォームでの ID/PWを作成するためのフォーム情報
     *            'errors'  => array(),  ---> ログインエラー情報
     *            'loginBackUrl' => String,   ---> 認証完了後に実行するアクション 
     *            'expire' => String,   ---> ログインの有効時間 strtotime() の形式で
     *            '' => '',
     *           )
     */
    var $authData = array();
    var $authErrors = array();
    var $authDataErrorSessName = 'errors';
    var $authLoginBackUrlSessName = 'loginBackUrl';
    var $authExpireSessName    = 'expire';
    
    /* 認証エラー関係 */
    var $AUTH_NORMAL        = 0;
    var $AUTH_IS_NOT_LOGIN  = 1;
    var $AUTH_LOGIN_ID_ERR  = 2;
    var $AUTH_PASSWORD_ERR  = 3;
    var $AUTH_OVER_LIFETIME = 4;
    var $errMsgs = array(0  => '',
                         1  => 'ログインして下さい。',
                         2  => 'ユーザーIDが正しくありません。',
                         3  => 'パスワードが正しくありません。',
                         4  => 'ログイン有効時間をすぎたためログアウトしました。再度ログインして下さい。'
                        );
    
    /** StartUp */
    function startup(&$controller) {
        $this->controller =& $controller;
        
        // 初期処理
        $this->_init();
        
        // 認証処理を行うアクションの場合は処理の開始
        if (in_array($controller->action, $this->requireAuth)) {
            $this->start();
        } else {
            $this->setLoginBackUrl();
        }
        
        // authDataをセッションに書き出す
        $this->Session->write($this->sessionName, $this->authData);
    }
    
    
    /**
     * 初期処理
     */
    function _init() {
        //
        // 利用するモデルの定義
        // 認証データが格納されているモデルを定義
        //
        loadModel($this->authModelName);
        $className = $this->authModelName;
        $this->authModel =& new $className();
        
        // セッションに認証データがある場合は $authDataに値を設定
        if ($this->Session->check($this->sessionName)) {
            $this->authData = $this->Session->read($this->sessionName);
        }
        
        // 別名参照の作成
        // $this->authData[xxxxx] と書くのを省略したいだけw
        $this->authErrors =& $this->authData[$this->authDataErrorSessName];
        $this->authExpire =& $this->authData[$this->authExpireSessName];
        $this->authLoginBackUrl =& $this->authData[$this->authLoginBackUrlSessName];
    }
    
    
    /**
     * 認証制限するための action名を設定
     * 
     * @params  array   引数で定義されたアクション名は認証制限をかける物とする
     */
    function requireAuth() {
        $this->requireAuth = func_get_args();
    }

    
    /**
     * 認証処理の開始
     */
    function start() {
        // 認証開始時にエラー情報破棄(ログインエラー時用にエラー情報をセッションで渡しているため)
        $this->authData[$this->authDataErrorSessName] = array();
        
        // 
        // ログインフォームから入った場合は
        // ログイン処理を実行
        //
        if ($this->_isLoginExecute()) {
            if(!$this->login($this->controller->data[$this->authFormName][$this->authLoginIdField],
                            $this->controller->data[$this->authFormName][$this->authPasswordField])) {
                // ログイン失敗時
                $this->_redirectLoginForm();
            } else {
                // 別名参照の作成
                // ログイン成功時はログイン実行によって取得した会員情報を authDataに渡す
                $this->authData[$this->authModelName] =& $this->authMember[$this->authModelName];
                // ログイン有効時間を更新
                $this->setExpire();
            }
        } else {
            // 別名参照の作成
            // ログイン画面を経由していない場合は authMember にセッションに持っていた会員情報データを渡す
            $this->authMember[$this->authModelName] =& $this->authData[$this->authModelName];
        }
        
        //
        // 未ログイン時はログイン画面を表示
        //
        if  (!$this->checkAuth()){
            // ログイン完了後戻るべき URLをセット
            $this->setLoginBackUrl();
            
            // ログインエラーをセット
            $this->setError($this->AUTH_IS_NOT_LOGIN);
            $this->authData[$this->authFormName] = array();
            
            // ログインフォームへリダイレクト
            $this->_redirectLoginForm();
        }
        
        //
        // ログインしていても有効時間を過ぎていれば
        // やっぱりログイン画面を表示
        //
        if (!$this->checkExpire()) {
            $this->setError($this->AUTH_OVER_LIFETIME);
            $this->authData[$this->authFormName] = array();
            $this->_redirectLoginForm();
        }
        
        //
        // 問題なければ、認証時刻を記録
        //
        $this->setExpire();
    }
    
    /**
     * ログイン処理
     * 
     * @param   String  $login_id
     * @param   String  $password
     * @return  Boolean True: ログイン成功 / False: ログイン失敗
     */
    function login($login_id, $password) {
        if (empty($login_id)) {
            $this->setError($this->AUTH_LOGIN_ID_ERR);
            return false;
        }
        
        //
        // ユーザーID / パスワードのチェック
        //
        $this->authMember =& $this->authModel->find(array($this->authLoginIdField => $login_id));
        if (empty($this->authMember)) {
            $this->setError($this->AUTH_LOGIN_ID_ERR);
            return false;
        }
        
        if ($this->authMember[$this->authModelName][$this->authPasswordField] != $password) {
            unset($this->authMember[$this->authModelName]);
            $this->authData[$this->authFormName][$this->authLoginIdField] = $login_id;
            $this->authData[$this->authFormName][$this->authPasswordField] = $password;
            
            // ログインエラーメッセージを表示してログイン画面を表示
            $this->setError($this->AUTH_PASSWORD_ERR);
            return false;
        }
        
        return true;
    }
    
    /**
     * ログアウト処理
     */
    function logout() {
        // セッションデータを削除する
        $this->Session->delete($this->sessionName);
    }
    
    
    /**
     * ログインされているかどうかのチェックを行う
     */
    function checkAuth() {
        return ($this->Session->check($this->sessionName) and !empty($this->authData[$this->authModelName]));
    }
    
    
    
    /**
     * ログイン有効時間をセットする
     * セットされた時間を過ぎるとログイン無効となる
     */
    function setExpire() {
        // authLifeTime (単位: authLifeTimeUnit) から有効時間を算出
        $this->authExpire = strtotime('+'.$this->authLifeTime.' '.$this->authLifeTimeUnit);
    }
    
    /**
     * ログイン時間が有効かどうかをチェックする
     * 
     * @return  boolean     True: 有効なログイン
     */
    function checkExpire() {
        // authExpireが現在時刻いないであれば有効とする
        return (strtotime('now') < $this->authExpire);
    }

    
    /**
     * ログインエラーをセットする
     * 
     * @param   Integer $errno  ログインエラーNo.
     */
    function setError($errno) {
        // ログインエラーNo. のメッセージも含めて
        // authErrors に対して格納
        $this->authErrors[$errno] = $this->errMsgs[$errno];
    }
    
    
    /**
     * ログインエラー情報を取得する
     * 
     * @return  array    array(errno => errmsg, errno => errmsg, ...)
     */
    function getError() {
        return $this->authErrors;
    }



    /**
     * ログインフォームから遷移したかどうか?
     * 
     * @return  boolean     true: ログイン画面からの遷移
     */
    function _isLoginExecute() {
        // authModelName を名称とした POSTデータがわたる時はログインフォームからの遷移とする
        return !empty($this->controller->data[$this->authFormName]);
    }


    /**
     * ログイン完了後に戻るべきURLへリダイレクト
     */
    function _redirectLoginBack() {
        $this->Session->write($this->sessionName, $this->authData);
        $login_back_url = !empty($this->authLoginBackUrl) ? $this->authLoginBackUrl : '/';
        $this->controller->redirect($login_back_url);
    }


    /**
     * ログイン画面へリダイレクトする
     * ログイン画面で表示するエラーメッセージ、入力フォームデータをセッションに格納し
     * リダイレクト
     */
    function _redirectLoginForm() {
        $this->Session->write($this->sessionName, $this->authData);
        $this->controller->redirect($this->loginFormUrl);
    }
    
    
    /*
     * Setter/Getter
     */
    function getAuthData($field = null) {
        if ($this->checkAuth()) {
            if ($field == null) {
                return $this->authData[$this->authModelName];
            } else {
                return $this->authData[$this->authModelName][$field];
            }
        } else {
            return false;
        }
    }
    function setAuthData($field, $value) {
        if ($this->checkAuth()) {
            $this->authData[$this->authModelName][$field] = $value;

            // authDataをセッションに書き出す
            $this->Session->write($this->sessionName, $this->authData);
        }
    }
    
    
    
    
    /**
     * フォームを中得する
     */
    function getLoginFormData() {
        if ($this->checkAuth()) {
            return $this->authData[$this->authFormName];
        } else {
            return false;
        }
    }
    
    
    /**
     * 実行中のURLを取得する
     * 実行したアクションに戻れるように
     * 
     * @return String  アクションURL
     */
    function getLoginBackUrl() {
        if ($this->checkAuth()) {
            return $this->authData[$this->authLoginBackUrlSessName];
        } else {
            return false;
        }
    }
    
    
    /**
     * 実行中のURLを取得する
     * 実行したアクションに戻れるように
     * 
     * @return String  アクションURL
     */
    function setLoginBackUrl() {
        if (getenv('REQUEST_URI') != $this->loginFormUrl) {
            // ログイン完了後戻るべき URLを現在のURLとして 
            $this->authLoginBackUrl = getenv('REQUEST_URI');
        }
    }
}

CakePHP 登録フォームの実装 其の三

12月 3, 2007 · Posted in CakePHP, PHP · 1 Comment 

確認画面画面前の入力チェック。
CakePHPの入力チェックは Model部分にて クラス変数 $validate を定義することで save()メソッドによる更新時、validates() メソッドによる明示的な操作のいずれかで入力チェックを行うことができる。
入力チェックの方式としてはあらかじめ定義されている、VALID_NOT_EMPTY, VALID_NUMBER, VALID_EMAIL, VALID_YEAR で行えるのと、正規表現を利用することが出来る。定数 VALID_* もようは正規表現を定数定義してるだけだしね。

確認画面を表示する前の入力チェックでは validates() による明示的な入力チェックが基本となるか。
例えば、User モデルの入力チェックを行う場合コントローラー内で

  if($this->User->validates($this->data['User']) {
  // 無問題
  } else {
    $this->validateErrors($this-User);	// Viewで tagErrorMsgでメッセージを表示するための操作
    $this->render('フォーム');
  }

として、Viewでエラーメッセージを表示するように出来る View側では

<table class="registForm">
  <tr>
    <td>氏名</td><td><?php echo $html->input('User/name') ?><?php echo $html->tagErrorMsg('User/name', '氏名を入力して下さい'); ?></td>
</table>

とかしておくことで、name のバリデーションに引っかかった場合にのみ、’氏名を入力して下さい’ のメッセージが表示される。
この tagErrorMsg のエラーの吐き出しが、<div class=”error_message”></div> でエラーメッセージを表示してくれる。これによって CSSでerror_message を定義しておけば目立たせることなど出来る。個人的には <span> で囲ってほしかったわ。ここだけ <span> にしちゃいました。

また、入力エラーを目立たせたい場合以下のようにスタイルで class=”warning” などを使って、背景変えたり入力エラーの場所を目立たせることが多いので、

<table class="registForm">
  <tr class="worning">
    <td>氏名</td><td><?php echo $html->input('User/name') ?><?php echo $html->tagErrorMsg('User/name', '氏名を入力して下さい'); ?></td>
  </tr>
</table>

次のヘルパー追加して実現しました。

function tagErrorClass($field, $text) {
    $error = 1;
    $this->setFormTag($field);
    if ($error == $this->tagIsInvalid($this->model, $this->field)) {
        return sprintf('%s', is_array($text) ? (empty($text[$error - 1]) ? 'Error in field' : $text[$error - 1]) : $text);
    } else {
        return null;
    }
}

ま、tagErrorMsg を一部変えただけですね。呼び出す際は、

<table class="registForm">
  <tr class="<?php echo $hige->tagErrorClass('User/name', 'warning'); ?>">
    <td>氏名</td><td><?php echo $html->input('User/name') ?><?php echo $html->tagErrorMsg('User/name', '氏名を入力して下さい'); ?></td>
  </tr>
</table>

と言った感じで。

入力エラーの表示方法はどうも上記の方法しか用意されていないように思われる。探してみたところ。あったらすいません。
例えばフォームの上部にまとめて入力エラーの内容を表示するとか簡単にできそうもない。
このあたり、Validationに関しては結構使い勝手がもう一歩届かないというのが、現在使っているところでの感触だ。


Maple のプロジェクト再開のもよう

12月 2, 2007 · Posted in PHP · Comment 

なんというか、
Mapleから CakePHPへ移行でもすんべ(地元の方言チックに)
とブログに書いてみたところ

開発者である kunitさんのブログにて、Maple の活動を再開しようと思っているとのこと。

さて、どうすんべw

現状の感触では Maple の方が好みに合っている部分も多かったので再開がんばってとエールを送ってみる。

せっかくなので今まで Maple-UserのMLにしか入っていなかったが
Maple-Devなんかにも入って見た。