<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
		xmlns:xhtml="http://www.w3.org/1999/xhtml"
>

<channel>
	<title>MJ::Blog &#187; CakePHP</title>
	<atom:link href="http://www.majima.net/category/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.majima.net</link>
	<description>日々のあれこれをテキトーに</description>
	<lastBuildDate>Sun, 11 Mar 2012 14:25:18 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/category/cakephp/feed/" />
		<item>
		<title>CakePHP 認証機能</title>
		<link>http://www.majima.net/php/198/</link>
		<comments>http://www.majima.net/php/198/#comments</comments>
		<pubDate>Sun, 09 Dec 2007 14:00:39 +0000</pubDate>
		<dc:creator>MJ</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.majima.net/php/198/</guid>
		<description><![CDATA[ログイン認証出来る機能がほしかったのだが標準で実装されているACLって認証機能じゃなかったのね orz データベースなどのユーザーテーブル上のID/PWで認証できる機能を探していたところ、いろいろとモジュールが見つかり以 [...]]]></description>
			<content:encoded><![CDATA[<p>ログイン認証出来る機能がほしかったのだが標準で実装されているACLって認証機能じゃなかったのね orz<br />
データベースなどのユーザーテーブル上のID/PWで認証できる機能を探していたところ、いろいろとモジュールが見つかり以下が参考になった。</p>
<ul>
<li><a href="http://code.nanigac.com/source/view/323" target="_blank">CakePHPで自動ログインコンポーネント</a></li>
<li><a href="http://www.jamboree.jp/cms/archives/10" target="_blank">CakePHP::obAuthを使ったユーザ認証</a></li>
</ul>
<p>とはいえ、個人で実装するにはいろいろと足りないところもあったりしたので、いいところを真似しつつ自作してみた。<br />
それなりのレベルまで作れた感じな為公開してみることにした。不具合点などはご指摘下さい。<br />
またご利用に際しては自己責任でお願いします。</p>
<h3>使い方</h3>
<p>コンポーネントとして動作。controllers/components にauth.phpとして配置。</p>
<p>まずはコンポーネントを利用するためにコントローラのはじめに以下のように設定</p>
<pre>
  var $components = array('Auth');
</pre>
<p>また、認証が必要なアクションを設定。<br />
beforeFilter() メソッド中に認証が必要になるアクションを定義する方法を採用。</p>
<pre>
    function beforeFilter() {
        // 認証設定
        $this->Auth->requireAuth('userMenu', 'editUser');

        // 二重登録防止
        $this->Security->requireAuth('editUser');
    }
</pre>
<p>Securityコンポーネントと同様の方法で、認証が必要なアクションについて、requireAuth()の中にカンマ区切りでアクション名を羅列する方法としました。<br />
検索して見つけたもろもろのモジュールでは、すべてに対して認証をしていたり、アクションの実行時に呼び出しを行ったりと面倒な方法をしているのが多かったのと、Securityコンポーネントと同じ方法で対応した方がわかりやすかったという理由で、こうしてみた。<br />
Security->requireAuth()で二重投稿防止などをしている場合はその宣言より前に行う必要があります。</p>
<p>課題としては、無制限に認証をかける場合は allとか指定すると認証とか出来るといいのかもしれません。ま、今のところ必要ないから作ってないけどｗ</p>
<p>認証されているかをチェックする場合は、</p>
<pre>
  $this->Auth->checkAuth();
</pre>
<p>にてチェック。</p>
<p>ログアウトをする場合は、</p>
<pre>
  $this->Auth->logout();
</pre>
<p>とする感じで。</p>
<p>また、認証は &#8216;member&#8217;テーブルの &#8216;userid&#8217; / &#8216;password&#8217; を利用して行い、該当するユーザーが見つかった場合は &#8216;member&#8217;テーブル上のそのデータを認証ユーザーデータとして保存します。<br />
実際は &#8216;Member&#8217;モデルで find()したユーザーデータが配列として保存されます。それらの値を取得する場合は、</p>
<pre>
  $value = $this->Auth->getAuthData('フィールド名');
</pre>
<p>member_name というフィールドの値を取りたい時は getAuthData(&#8216;member_name&#8217;) で取得。</p>
<h3>コンポーネント側の設定</h3>
<p>$authFormName:<br />
ま、authFormNameという命名が正しいかどうかは微妙です。<br />
ログインフォームで $html->input(&#8216;Login/userid&#8217;) などのように指定する際のモデル名(左では&#8217;Login&#8217;)を指定。</p>
<p>$authModelName:<br />
認証で指定するモデル（テーブル）名。</p>
<p>$authLoginIdField:<br />
ユーザーIDが格納されているフィールド名。 $authModelNameテーブルの $authLoginIdFieldがIDということになります。</p>
<p>$authPasswordField:<br />
パスワードが格納されているフィールド名。 $authModelNameテーブルの $$authPasswordFieldがパスワードということになります。</p>
<p>$authLifeTime:<br />
$authLifeTimeUnit:<br />
上が認証有効期間、下がその単位。60分の場合は<br />
$authLifeTime = 60;<br />
$authLifeTimeUnit = &#8216;minutes&#8217;;<br />
となる。strtotime() で比較するのでそこで認識できる単位を指定する。</p>
<p>$loginFormUrl:<br />
ログインフォームURL。ログインが必要な場合はこのURLにリダイレクトされる。</p>
<p>$sessionName:<br />
認証情報を保存するためのセッション名。ま、適当に。</p>
<p>ログインエラーとしては ID不正、パスワード不正、認証有効期間オーバーを用意しており、それらが<br />
$this->Auth->getError() で取得出来るので、取得した内容をログインエラーとして表示できるんだけど、この辺がもうちょっとうまいこと実現できないかなーと思っているところ。<br />
現状、たとえば loginForm() というアクションを用意して</p>
<pre>
    function loginForm() {
        $this->data[$this->Auth->authFormName] = $this->Auth->getLoginFormData();
        $this->set('errMsgs', $this->Auth->getError());
    }
</pre>
<p>のように errMsgsにエラー情報を渡してログイン画面を表示する必要があり、一つアクションに命令を各必要がある。<br />
できればここは、書かずに処理したいんですよね。コンポーネント側だけで解決出来るといいかなーと。</p>
<p>書くべきところはこんな感じかしら？</p>
<h3>ソース[auth.php]</h3>
<pre>

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;

    /** ログインフォームへのＵＲＬ */
    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(&#038;$controller) {
        $this->controller =&#038; $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 =&#038; new $className();

        // セッションに認証データがある場合は $authDataに値を設定
        if ($this->Session->check($this->sessionName)) {
            $this->authData = $this->Session->read($this->sessionName);
        }

        // 別名参照の作成
        // $this->authData[xxxxx] と書くのを省略したいだけｗ
        $this->authErrors =&#038; $this->authData[$this->authDataErrorSessName];
        $this->authExpire =&#038; $this->authData[$this->authExpireSessName];
        $this->authLoginBackUrl =&#038; $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] =&#038; $this->authMember[$this->authModelName];
                // ログイン有効時間を更新
                $this->setExpire();
            }
        } else {
            // 別名参照の作成
            // ログイン画面を経由していない場合は authMember にセッションに持っていた会員情報データを渡す
            $this->authMember[$this->authModelName] =&#038; $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 =&#038; $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]);
    }

    /**
     * ログイン完了後に戻るべきＵＲＬへリダイレクト
     */
    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  アクションＵＲＬ
     */
    function getLoginBackUrl() {
        if ($this->checkAuth()) {
            return $this->authData[$this->authLoginBackUrlSessName];
        } else {
            return false;
        }
    }

    /**
     * 実行中のURLを取得する
     * 実行したアクションに戻れるように
     *
     * @return String  アクションＵＲＬ
     */
    function setLoginBackUrl() {
        if (getenv('REQUEST_URI') != $this->loginFormUrl) {
            // ログイン完了後戻るべき URLを現在のURLとして
            $this->authLoginBackUrl = getenv('REQUEST_URI');
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.majima.net/php/198/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/php/198/" />
	</item>
		<item>
		<title>CakePHP 登録フォームの実装　其の三</title>
		<link>http://www.majima.net/php/197/</link>
		<comments>http://www.majima.net/php/197/#comments</comments>
		<pubDate>Mon, 03 Dec 2007 13:21:22 +0000</pubDate>
		<dc:creator>MJ</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.majima.net/php/197/</guid>
		<description><![CDATA[確認画面画面前の入力チェック。 CakePHPの入力チェックは Model部分にて クラス変数 $validate を定義することで save()メソッドによる更新時、validates() メソッドによる明示的な操作の [...]]]></description>
			<content:encoded><![CDATA[<p>確認画面画面前の入力チェック。<br />
CakePHPの入力チェックは Model部分にて クラス変数 $validate を定義することで save()メソッドによる更新時、validates() メソッドによる明示的な操作のいずれかで入力チェックを行うことができる。<br />
入力チェックの方式としてはあらかじめ定義されている、VALID_NOT_EMPTY, VALID_NUMBER, VALID_EMAIL, VALID_YEAR で行えるのと、正規表現を利用することが出来る。定数 VALID_* もようは正規表現を定数定義してるだけだしね。</p>
<p>確認画面を表示する前の入力チェックでは  validates() による明示的な入力チェックが基本となるか。<br />
例えば、User モデルの入力チェックを行う場合コントローラー内で</p>
<pre>
  if($this->User->validates($this->data['User']) {
  // 無問題
  } else {
    $this->validateErrors($this-User);	// Viewで tagErrorMsgでメッセージを表示するための操作
    $this->render('フォーム');
  }
</pre>
<p>として、Viewでエラーメッセージを表示するように出来る View側では</p>
<pre>
&lt;table class="registForm"&gt;
  &lt;tr&gt;
    &lt;td&gt;氏名&lt;/td&gt;&lt;td&gt;&lt;?php echo $html-&gt;input('User/name') ?&gt;&lt;?php echo $html-&gt;tagErrorMsg('User/name', '氏名を入力して下さい'); ?&gt;&lt;/td&gt;
&lt;/table&gt;
</pre>
<p>とかしておくことで、name のバリデーションに引っかかった場合にのみ、&#8217;氏名を入力して下さい&#8217; のメッセージが表示される。<br />
この tagErrorMsg のエラーの吐き出しが、&lt;div class=&#8221;error_message&#8221;&gt;&lt;/div&gt; でエラーメッセージを表示してくれる。これによって CSSでerror_message を定義しておけば目立たせることなど出来る。個人的には &lt;span&gt; で囲ってほしかったわ。ここだけ &lt;span&gt; にしちゃいました。</p>
<p>また、入力エラーを目立たせたい場合以下のようにスタイルで class=&#8221;warning&#8221; などを使って、背景変えたり入力エラーの場所を目立たせることが多いので、</p>
<pre>
&lt;table class="registForm"&gt;
  &lt;tr class="worning"&gt;
    &lt;td&gt;氏名&lt;/td&gt;&lt;td&gt;&lt;?php echo $html-&gt;input('User/name') ?&gt;&lt;?php echo $html-&gt;tagErrorMsg('User/name', '氏名を入力して下さい'); ?&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>次のヘルパー追加して実現しました。</p>
<pre>
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;
    }
}
</pre>
<p>ま、tagErrorMsg を一部変えただけですね。呼び出す際は、</p>
<pre>
&lt;table class="registForm"&gt;
  &lt;tr class="&lt;?php echo $hige-&gt;tagErrorClass('User/name', 'warning'); ?&gt;"&gt;
    &lt;td&gt;氏名&lt;/td&gt;&lt;td&gt;&lt;?php echo $html-&gt;input('User/name') ?&gt;&lt;?php echo $html-&gt;tagErrorMsg('User/name', '氏名を入力して下さい'); ?&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>と言った感じで。</p>
<p>入力エラーの表示方法はどうも上記の方法しか用意されていないように思われる。探してみたところ。あったらすいません。<br />
例えばフォームの上部にまとめて入力エラーの内容を表示するとか簡単にできそうもない。<br />
このあたり、Validationに関しては結構使い勝手がもう一歩届かないというのが、現在使っているところでの感触だ。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.majima.net/php/197/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/php/197/" />
	</item>
		<item>
		<title>CakePHP 登録フォームの実装　其の二</title>
		<link>http://www.majima.net/php/195/</link>
		<comments>http://www.majima.net/php/195/#comments</comments>
		<pubDate>Mon, 26 Nov 2007 14:25:25 +0000</pubDate>
		<dc:creator>MJ</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.majima.net/php/195/</guid>
		<description><![CDATA[続きです。続けられました（１週間空いたけど）ｗ 登録フォームの次は確認画面の表示です。厳密に言えば入力チェックが挟みますが、ひとまず確認画面ってことで。 確認画面の表示 &#60;input type=&#8221;hid [...]]]></description>
			<content:encoded><![CDATA[<p>続きです。続けられました（１週間空いたけど）ｗ<br />
登録フォームの次は確認画面の表示です。厳密に言えば入力チェックが挟みますが、ひとまず確認画面ってことで。</p>
<ul>
<li>確認画面の表示</li>
<li>&lt;input type=&#8221;hidden&#8221; &#8230;/&gt; の自動生成</li>
</ul>
<p>といった内容になっています。</p>
<p>確認画面、それは入力した内容を確認のため表示するという至極単純なページ。<br />
単純なわりに、フォームと似て非なるものをつくるため工数がかさみます。</p>
<p>CakePHPではそこがちょっとは楽になるかなーと思ったのですが、特にこのありがちな流れを補完するような機能はないようですね。orz<br />
とはいえ楽な部分もあります。<br />
フォーム表示では、テキストボックスなどを表示するのに  Htmlヘルパーを利用して、</p>
<pre>
  氏名： < ?php echo $html->input('Member/name'); ?>
  フリガナ： < ?php echo $html->input('Member/kana'); ?>
</pre>
<p>などとしますが、確認画面を表示する際はこのフォームの*.thtmlファイルをコピペして</p>
<pre>
  氏名： < ?php echo $html->tagValue('Member/name'); ?>
  フリガナ： < ?php echo $html->tagValue('Member/kana'); ?>
</pre>
<p>とすれば事足りる模様。これは置換すればいいだけで楽。 &lt;input /&gt; タグを強引に正規表現で置換してちょっと直すよりはずっといけてます。<br />
プルダウンの表示などはこれだけでは済みませんが、これはこれで大分作業が楽ですね。</p>
<p>もう一つ必要な処理が、次の実行処理に必要なフォーム入力値の引き渡し。<br />
今までは、&lt;input type=&#8221;hidden&#8221; &#8230; /&gt; を使ってたのですが、たくさんの入力項目があったりすると、すべてを記述していくのは面倒なもんです。<br />
で、世の識者はどういう風にしているのかを調べてみると・・・</p>
<ul>
<li>確認画面表示時にセッションに保存し、実行時にセッションから引っ張る</li>
<li>登録内容をひとまとめにして base64 などにエンコードして一つの hidden タグにまとめ、実行時にデコードして解析</li>
<li>普通に hidden タグをゴリ書き</li>
</ul>
<p>といったところのようです。どれも一長一短ですかね？セキュリティ面では２番目などがよいのかもしれません。セッションは何となく気分が悪いです（好みの問題）。<br />
ま、自分は軽くオヤジはいっている古い人間なので、結局 3つめの方法に落ち着くことにした。<br />
とはいえ、一つ一つ書くのはバカバカしいわけで考えてみたところ、POSTされた値から自動生成出来ますよね？（Ethnaではそういうメソッドがあるようでした）</p>
<p>というわけで独自にヘルパー作りました。内容は以下の通り</p>
<pre>
class HigeHelper extends Helper {
    var $helpers = array('Html');        // < -- Htmlヘルパー利用するため

    /**
     * 確認画面用の Hiddenタグを返す
     *
     * @param   String  $modelName   モデル名
     * @return  String
     */
    function hiddenVars($modelName) {
        $ret = "";
        foreach ($this->data[$modelName] as $key => $val) {
            $ret .= $this->Html->hidden("$modelName/$key")."\n";
        }
        return $ret;
    }
}
</pre>
<p>超簡単でした。ヘルパー名は適当。髭</p>
<p>はじめの氏名・フリガナの用にモデル名を Member としてフォームを生成している場合は、</p>
<pre>
  < ?php echo $hige->hiddenVars('Member'); ?>
</pre>
<p>でＯＫ。</p>
<p>たぶんこれでいけているはず。不具合あればご連絡下さい。またご利用は計画的に。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.majima.net/php/195/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/php/195/" />
	</item>
		<item>
		<title>CakePHP  登録フォームの実装　（なぜか）其の一</title>
		<link>http://www.majima.net/php/194/</link>
		<comments>http://www.majima.net/php/194/#comments</comments>
		<pubDate>Wed, 21 Nov 2007 14:04:07 +0000</pubDate>
		<dc:creator>MJ</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.majima.net/php/194/</guid>
		<description><![CDATA[いろいろと動かしては悩んでということを繰り返しています。CakePHP。 それなりに使えてきたのではと思います。 さて、CakePHP習得まずはじめに何をしたかというと Webアプリの基本のフォーム送信ですね。会員登録で [...]]]></description>
			<content:encoded><![CDATA[<p>いろいろと動かしては悩んでということを繰り返しています。CakePHP。<br />
それなりに使えてきたのではと思います。</p>
<p>さて、CakePHP習得まずはじめに何をしたかというと<br />
Webアプリの基本のフォーム送信ですね。会員登録でメール送信というアレ。<br />
ちまたのCakePHPの解説とかホントか読むと、コントローラーで  add() メソッド作って、 $this->data が送られてきたらフォームからの送信と見なして処理実行・・・というのが多く、フォーム > 確認 > 完了 という流れはあまりサンプルがなかった。</p>
<p>次のような感じですね。</p>
<pre>
class HigeContoroller extends AppController {
    略
    function add() {
        if (empty($this->data)) {
            // フォームデータからDB登録

        }
        ・・・・フォーム表示処理
    }
}
</pre>
<p>まあ、限られたスペースでの解説というのもありますが、上記の通りに作った場合、http://do.ma.in/hige/add というURL一つでフォーム表示・登録実行がなされます。二つの処理が同じURLだとログ解析時に同じ各処理が同じURLとして処理されるためあまりうまくない。フォームまで表示しても、その後の実行がないなどが分かれば、フォームに問題があるなどがわかるからね。従って、ここを分けるようにする。で、単なる会員登録ということで、registForm > registConf > registComplete としてみた。<br />
毎回こういう英語で悩むのよね。Confと略すかConfirmとするか、Completeでいいの？とか。あとはキャメルケースでいいのか？アンダーバー（regist_formのように）で結んだ方がよいのか・・・などなど。誰かがこうせいというのを出してくれれば真似するのですがｗ</p>
<p>さて、フォーム表示ですが Htmlヘルパーを利用してがりがりと書いています。<br />
DreamWeaverなどで見ようとするとうまくなさそうですが、結構便利ですね。<br />
毎回 < ?php echo $html->なんちゃら(); ?> のように echo しないといけないのが何となくめんどくさい。とおもったら、AUTO_OUTPUT定数で変えられる模様。AUTO_OUTPUT = true にしてみたら、 $html->formTag がきちんと出力されないんですけど．．．</p>
<p>Htmlヘルパーでまずつまづいたのが、日付のプルダウン表示。<br />
$html->dateTimeOptionTag(&#8216;Member/birth&#8217;, &#8216;YMD&#8217;, &#8216;NONE&#8217;); としてみたところ、月は英語表記 orz。つかえねー<br />
調べてみたところいくつかやり方はあるようですが、ま、日本語日付ようのヘルパーメソッドを作るというのが基本のようで、あとは</p>
<ul>
<li>もとの Htmlヘルパー(/cake/libs/view/helpers/html.php)を直接いじる</li>
<li>/app/views/helpers に上記 html.php をコピっていじる。/app/views/helpers/html.php を優先して呼ぶ模様（未検証）</li>
<li>独自のヘルパーを作る</li>
</ul>
<p>１番目の元をいじるのは嫌だし、２番目は元は残ってるとはいえ、CakePHPがバージョンアップして Htmlヘルパーが改善した時にめんどくさそう。ということで３番目を採用。</p>
<p>とりいそぎ、HtmlJp というヘルパー（名前適当）を作るということで、、、以下のようになりました。<br />
ま、Htmlヘルパーの monthOptionTag, dateTimeOptionTag の一部を変更しただけです。</p>
<pre style="height: 20em;">
class HtmlJpHelper extends Helper {     // < -- HtmlHelper を extends してもよいのかしら？
    var $helpers = array('Html');	// Htmlヘルパー利用するので

    // 月のプルダウン表示
    function monthOptionTagJp($tagName, $value = null, $selected = null, $selectAttr = null, $optionAttr = null, $showEmpty = true) {
        if (empty($selected) &#038;&#038; ($this->Html->tagValue($tagName))) {			// < -- このあたり $this->tagValue を $this->Html->tagValueに。
            $selected = date('m', strtotime($this->Html->tagValue($tagName)));
        }
        $monthValue = empty($selected) ? ($showEmpty ? NULL : date('m')) : $selected;
        $months = array('01' => '1', '02' => '2', '03' => '3', '04' => '4', '05' => '5', '06' => '6', '07' => '7', '08' => '8', '09' => '9', '10' => '10', '11' => '11', '12' => '12');	   // < -- 英語表記やめ

        return $this->Html->selectTag($tagName . "_month", $months, $monthValue, $selectAttr, $optionAttr, $showEmpty);
    }

    function dateTimeOptionTagJp($tagName, $dateFormat = 'DMY', $timeFormat = '12', $selected = null, $selectAttr = null, $optionAttr = null, $showEmpty = true) {
        // ------ 略 ------  上記と同じく  $this->なんちゃら　は $this->Html->なんちゃらに・・・
        switch($dateFormat) {
            case 'DMY': // so uses the new selex
                $opt = $this->Html->dayOptionTag($tagName, null, $day, $selectDayAttr, $optionAttr, $showEmpty) . '日' .
                $this->monthOptionTagJp($tagName, null, $month, $selectMonthAttr, $optionAttr, $showEmpty) . '月' . $this->Html->yearOptionTag($tagName, null, null, null, $year, $selectYearAttr, $optionAttr, $showEmpty).'年';
        // 以下同じように単純に、年月日というのを付けるだけ
}</pre>
<p>といったところでしょうか。<br />
あとは、コントローラーで忘れずに</p>
<pre>
  var $helpers = array('HtmlJp');</pre>
<p>をつけて、Viewファイルで  $htmlJp->dateTimeOptionTagJP() を呼び出す。<br />
電話番号などいくつかのテキストボックスに分けるような時も一工夫必要かもしれません。</p>
<p>なんだか長くなってしまったので、続く、、、</p>
]]></content:encoded>
			<wfw:commentRss>http://www.majima.net/php/194/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/php/194/" />
	</item>
		<item>
		<title>CakePHPを習得中</title>
		<link>http://www.majima.net/php/193/</link>
		<comments>http://www.majima.net/php/193/#comments</comments>
		<pubDate>Mon, 19 Nov 2007 14:13:24 +0000</pubDate>
		<dc:creator>MJ</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.majima.net/php/193/</guid>
		<description><![CDATA[自分で構築する場合、PHPのフレームワークはMapleで落ち着こうかと思っていたのだけど、いろいろあって現在CakePHPに手を出している。 もともとMapleを選択したのは、自作していたフレームワークと設定ファイル等の [...]]]></description>
			<content:encoded><![CDATA[<p>自分で構築する場合、PHPのフレームワークは<a href="http://kunit.jp/maple/" target="_blank">Maple</a>で落ち着こうかと思っていたのだけど、いろいろあって現在<a href="http://www.cakephp.org/" target="_blank">CakePHP</a>に手を出している。</p>
<p>もともと<a href="http://kunit.jp/maple/" target="_blank">Maple</a>を選択したのは、自作していたフレームワークと設定ファイル等の扱い方・考え方が近かったのと、日本純正という理由からだった。Ethnaも合ったけどいまいち肌に合わなかった。ようは相性の問題である。<br />
何げに使えていたのだが、最近の動向を見るに<a href="http://kunit.jp/maple/" target="_blank">Maple</a>周りの動きがほとんどないというのと、<a href="http://www.cakephp.org/" target="_blank">CakePHP</a>の話題をあちこちで見るようになり「うーむ、つかいやすそうだなー」と感じたため、乗り換えるかーと思った次第である。</p>
<p>丁度、大分前に作ったシステムを作り直すという仕事が入っているため、そいつをぶっつけ本番で<a href="http://www.cakephp.org/" target="_blank">CakePHP</a>化すんべ！！と現在鋭意制作中だったりする。</p>
<p>ここ１〜２週間他の仕事もこなしつつコーディングをしているのだが感触としては「うんそれなりに使いやすいね」といったところである。<br />
バリデーションなんかは<a href="http://kunit.jp/maple/" target="_blank">Maple</a>の方が便利だったりするが、設定ファイルを使わないというのは結構楽なんだなーと実感した次第である。<br />
現在のところ、登録フォーム〜完了までとかメールの送信とか認証機能とか、試行錯誤しつつ<a href="http://www.cakephp.org/" target="_blank">CakePHP</a>のソース見つつ調べつ実装してきたので、大分時間かかった感はあるが、以後はそんなに頭を悩ますこともないだろうから、実際の開発スピードを実感できるのはこれからかもしれない。<br />
しかし、本格的なコーディングは１年ぶりくらいだったりするので、開発スピード結構落ちてるね。リハビリ、リハビリ。</p>
<p>今のところの実感としては</p>
<ul>
<li>URLによって動作振り分けられるのは快適〜</li>
<li>コーディングする分量は確かに減るかもしれない</li>
<li>View にPHPのソースを詰め込んでいくと、DreamWeaverなどでソースを見る時にどうもなーと思う点。かといって、元々持っている便利なヘルパーを使わないのもどうもなーという感じ</li>
<li>Model の部分が個人的には微妙。findAll() とか確かに便利だが今一歩かゆいところに手が届かない〜</li>
</ul>
<p>などなどいろいろと感じることがあったので、以後、<a href="http://www.cakephp.org/" target="_blank">CakePHP</a>ネタ続くかもしれません。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.majima.net/php/193/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<xhtml:link rel="alternate" media="handheld" type="text/html" href="http://www.majima.net/php/193/" />
	</item>
	</channel>
</rss>

