top


総投稿数 本 
no_

スポンサーサイト

 --------
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
カテゴリ :スポンサー広告 トラックバック(-) コメント(-)
no_1118

ログイン:我流の習得方法。良きソースに近道はある。プロジェクトにログイン機能を取り入れるための学習方法 ・・・ 【symphonyで、創るぞ】

 2009-10-14
 symfonyでのサイト開発ネタ。
 プロジェクトに「ログイン」機能が必要になった。

 【今回のテーマ】
   ★どの頁からもログインできる機能
    ログイン後、専用の管理レイアウトに移動する

 ・今まで、PHPでセッション利用してゴリゴリ書いていた方法をやめ、
  せっかく学んでいる「symfony」でのやり方を探ってみる。


 我流、です。
 こういう使いこなれた機能の場合、まずは先人の模範例を
 参考にする。


 ★1:
 まず、素晴らしい出来栄えの(まだ、すべてを終えていない)
 チュートリアル
 ・The Askeet Tutorial(5日目: フォームとページャ)
  これを、実践のメインベースに据え、


 ★2:
 ・Practical symfony(10日目: フォーム)
 ・symfony Forms in Action(第1章 - フォームの作成)
  これらを同時参照し


 ★3:
 マニュアル
 ・The Definitive Guide to symfony 1.2
  で、新しく得た知識を納得・理解しつつ


 ★4:
 Doctrine
 ・The symfony and Doctrine book(6章 - データを扱う)
 ・The symfony Cookbook(データをDoctrineで読み取る)
  などに、横目を走らせる


 ★5:
 そしてさらに・・・
 ・(こいつは、追記の最後に)


  追記に ▼(長文ご容赦)


 ※今までのご参考

FC2ブログランキング にほんブログ村 IT技術ブログ Webサイト構築へ ブログ王ランキング 人気ブログランキング 人気ホームページランキングへ


more

************************************************
◎ どの頁からもログインできる機能、の巻:symphonyで、創るぞ
************************************************


 ※The Askeet Tutorial(5日目: フォームとページャ) に習う。


 ■ログインフォーム の配置


  ・アプリケーションのすべてのページからログインできるように
   グローバルレイアウトの
    (プロジェクト名)/appas/frontend/templates/layout.php に組込
   ・チュートリアルでは、ここに「リンクテキスト」を組み込むようにしているが
<li><?php echo link_to('sign in', 'user/login') ?></li>

    直接フォームを 常配置としたし。
   □これは後にして、先に「userモジュールを作成」する。
$ symfony init-module frontend user
 とあるけれども
    私はDoctrine 利用しているので、
    ・Userテーブル用意し
    ・config/doctrine/schema.yml に構造記載した上で、
$ symfony doctrine:build-model
$ symfony doctrine:build-form
$ symfony doctrine:generate-module frontend user User

    □指示に従い、不要な
     ・テンプレート:indexSuccess.php を削除
     ・アクション:actions.class.php の
      public function executeIndex を削除
   ・user/loginアクションを作成。
	public function executeLogin()
{
$this->getRequest()->setAttribute('referer', $this->getRequest()->getReferer());
return sfView::SUCCESS;
}

    ※リクエスト属性のリファラーを保存
     (ログイン成功後にフォームのターゲットアクションが
      オリジナルのリファラーにリダイレクト)
     ここは少し用途が違うので、実行せず理解のみ。
    □return sfView::SUCCESS; により
     loginSuccess.phpテンプレート が表示される。

   □この流れも変更し、この loginSuccess.phpテンプレートを
    _loginform.php として改造
    (プロジェクト名)/appas/frontend/modules/user/templates/ に配置する


   □レイアウト上の共通ヘッダー部分で
<?php include_partial('user/loginform', array('form' => $form)) ?>
 として呼び出し。
    ●注意●
     しかし、このままではエラー
Fatal error: Call to undefined function form_tag() in ...
 ※宣言が必要のようだ。
   □_loginform.php において、
<?php use_helper('Form'); ?>
 を追加記述。
    > 表示されるようにはなった。
      表示されるようにはなった
    もちろんこのままではおかしいので、レイアウトをCSSで調整。

 ■ログインフォーム アクション


  ・上記で各頁の共通部分に表示されたフォームには
   <form method="post" action="/user/login"> とされている。

   □submit された際の、アクション部分を用意する。


    チュートリアルで示されたものを改造し、
return $this->redirect($this->getRequestParameter('referer', '@homepage'));

    最後に今の頁(referer)に飛ぶようにしておく。
    (未入力、バリデート不可時)
   □チュートリアルでは、Criteria という
    ・ORマッパーとして採用するPropelのオブジェクト
     が使われていて、
// フォーム投稿を処理する
$nickname = $this->getRequestParameter('nickname');
$c = new Criteria();
$c->add(UserPeer::NICKNAME, $nickname);
$user = UserPeer::doSelectOne($c);
 となっているが、
    Doctrineなので、ここを、
    ・The symfony and Doctrine book(6章 - データを扱う) を
     参考に変更し(finderマジックメソッド を利用)
// フォーム投稿を処理する
$nickname = $this->getRequestParameter('nickname');
$user = Doctrine::getTable('User')->findOneByAc_user($nickname);

 しかし、ここで方針転換。
 ■バリデート
  Doctrine習得するときに覚えた、一連の
  バリデート手続きに沿って方法論を変えたい。
    ・DAY5: ログインフォームとパジネーション を
     参考にさせていただき、大幅に変えてゆく。
    ※考え方として
     ・ユーザーフォームのクラスを用意し
      さらに、ログインユーザーフォームのクラス という
      考え方が気に入った。
      (追記:ところが、これはうまくゆかなかったんだけどな)


   ご掲載の内容を参照させていただき、
   整理して以下のように実装。


   □(プロジェクト名)lib/form/doctrine/UserForm.class.php
    の編集


    ※デフォルトでは、function configure() には何も記載ないが
     ここに
  public function configure()
{
// unset fields
$unset_fields = array(
'*****','*****','*****',
);
foreach ($unset_fields as $value) {
$this->offsetUnset($value);
}
・・・・・
 と、このフォームアクションで更新などしない
 フォームへの処理を記述し、
    // nickname
$key = 'nickname';
$this->widgetSchema[$key] = new sfWidgetFormInput();
$this->widgetSchema->setLabel($key, 'ニックネーム');
$this->validatorSchema[$key] = new sfValidatorString();
$_min = 6;
$this->validatorSchema[$key]->setMessage('min_length', sprintf('ニックネームの文字数は%d文字以上で入力してください', $_min));
$this->validatorSchema[$key]->setOption('min_length', $_min);
$_max = 50;
$this->validatorSchema[$key]->setMessage('max_length', sprintf('ニックネームの文字数は%d文字以内で入力してください', $_max));
$this->validatorSchema[$key]->setOption('max_length', $_max);
$this->validatorSchema[$key]->setOption('required', true);
$this->validatorSchema[$key]->setMessage('required', 'ニックネームは必須です');
 などのバリデートチェック部分を記載。
 (ここに表示したものは上記 exgear.jpさんのまんま。実際は
  かなり変えているが・・・)

   続けて


   □(プロジェクト名)lib/form/doctrine/LoginUserForm.class.php
    を新規作成
    ※前述「UserForm.class.php」を親クラス。
     ・継承(するはずなのだが、どこが違うかされない))
     ソースは一応載せるけれど、exgear.jpさんのまんま。
     ありがとうございます。
class LoginUserForm extends UserForm
{
public function configure()
{
// unset fields
$unset_fields = array(
'*****','*****','*****t','*****',
);
foreach ($unset_fields as $value) {
$this->offsetUnset($value);
}

// password
$key = 'password';
$this->widgetSchema[$key] = new sfWidgetFormInputPassword();
$this->widgetSchema->setLabel($key, 'パスワード');
$this->validatorSchema[$key] = new sfValidatorString();
$this->validatorSchema[$key]->setOption('required', true);
$this->validatorSchema[$key]->setMessage('required', 'パスワードは必須です');

// referer
$key = 'referer';
$this->widgetSchema[$key] = new sfWidgetFormHiden();
$this->validatorSchema[$key] = new sfValidatorPass();
// setDefault
$this->setDefaults(array(
'referer' => sfContext::getInstance()->getRequest()->getReferer(),
));
// check valid user
$this->validatorSchema->setPostValidator(new sfValidatorCallback(array(
'callback' => array($this, 'validUser'),
)));
}

}

   □バリデーダー記述部分は少し変えて、
  public function validUser($validator, $values)
{
// if nickname is empty, not check.
if (($values['*****'] == "")||($values['*****'] == "")) { return $values; }

// check if valid user.
$user = User::getValidUserBy*****($values['*****']);
if ($user->count() != 1 || $values['*****'] != $user[0]->get*****()) {
throw new sfValidatorError($validator, 'アカウント と パスワード をお確かめください。');
return $values;
}
// set auth
$sf_user = sfContext::getInstance()->getUser();
$sf_user->setLoginAuth(true, $user[0]);
return $values;
}

    ※本当は、教えていただいているように、sha1を利用した
     ハッシュ暗号化でやるべいなんだろうけど、テストデータが
     phpMyAdminからの直打ちなので今はこれで・・・


   □(プロジェクト名)apps/frontend/lib/myUser.class.php
    の編集


    ※上記、setLoginAuth の定義。
class myUser extends sfBasicSecurityUser
{
public function setloginAuth($is_login, $user)
{
$this->setAuthenticated($is_login);
$this->addCredential('subscriber');
$this->setAttribute('subscriber_id', $user->getId(), 'subscriber');
$this->setAttribute('nickname', $user->getNickname(), 'subscriber');
}
}
 このあたりはもうちょっと理解を深めなくてはならぬ。

 ■actions.class.php の最終改造
  (プロジェクト名)apps/frontend/modules/user/actions/actions.class.php
  ※チュートリアルや参照頁とは、ちと用法が違い、
   (今はまだ)個別の頁で利用するのではなく
   各頁に共通で差し込む方法なので、其れに合わせた対応。


   まず


   □ログアウトアクションの準備
public function executeLogout(sfWebRequest $request)
{
$this->getUser()->setLoginAuth(false);
$this->redirect(@homepage);
}
 これはそのまま。

   次に


   □エラーがあった場合、元の頁に戻ったときに
    JavaScriptでアラート出したいので、エラーメッセージ
    すべて取得。
    ※これには、yoshihi6の備忘録さんの方法を参考にさせていただき、
$this->errormes = $error_messages = array_map(
create_function('$e', 'return $e->getMessage();'),
$this->form->getErrorSchema()->getErrors());
 として配列化。

   □エラーメッセージがあった場合、
    エラーメッセージを持って戻れる? ここは試行錯誤。


    ・$this->forward('(元のモジュール)', '(元のアクション)'); ?
     いや、違うな。


    ・パラメータ持ってリダイレクト?
$params = array(
'*****' => $*****,
);
$this->redirect((戻りURL).'?'.http_build_query($params));
 違うな。
     ※リダイレクトって、アドレスに出さないで、POSTで
      行うっててきるんだろうか? ・・・ 新たな疑問も涌いたが。

    結局。
    セッション使って一時保存した上で、戻り先で利用>消去。


    ははは。また力技だ。
$temp_mes = "";
if(
(isset($this->errormes))
&&(sizeof($this->errormes)>0)
){
foreach($this->errormes as $key => $value){
if($value!=''){
if(($key=='*****')||($key=='*****')){ $temp_mes .= "・".$value."\\n";
}elseif(($key=='*****')||($key=='*****')){
}elseif($key=='_csrf_token'){
}else{ $temp_mes .= "・".$value."\\n"; }
}
}
}
if($temp_mes!=''){ $_SESSION['*****'] = $temp_mes;
}
$tempURL = "";
if($referer!=''){
if(($referer==WWWROOT)){ $tempURL = WWWROOT;
}else{ $tempURL = ereg_replace(WWWROOT,"",$referer);
}
}else{ $tempURL = WWWROOT; }
$this->redirect($tempURL, true); exit;
 ※条件に応じてセッションに保存しておく。
    元の頁の共有部品では、
<?
if(
(isset($_SESSION['*****']))
&&($_SESSION['*****']!='')
){ ?><script type="text/javascript">alert('<? echo $_SESSION['*****']; ?>');</script><?
$_SESSION['*****']=""; unset($_SESSION['*****']);
} ?>
 として利用。

   □ログイン後の処理。


    ・バリデートチェック抜けたら、
if ($this->form->isValid()) {
// to otherwork
・・・・・
exit;
}else{
・・・・・
 と抜けるわけだが、

    ・共有部品上では
     ・$sf_user->isAuthenticated(); # ログインできていれば 1
     ・$sf_user->getAttribute('*****', '', 'subscriber'); # ログインできているユーザー名取得
     を利用してハンドリング。


 これで、とりあえずの目的は達せられた。
 もっとスマートな、あるいはsymfony利用した正しい使い方は
 おいおい学んでゆくだろう。


最後に・・・


 前述している
> ★5:
> そしてさらに・・・
> ・(こいつは、追記の最後に)
について・・・


 ・我流で行うときは必ず実際のソース例を参照させていただく。
  ・現在、symfonyチュートリアル
   「The Askeet Tutorial」の右カラムにある
    ・askeet trac はリンクが切れている
     ・・・が、
    ・Askeetの全ソース はこちらに ちゃんとあり


  ・さらに
    ・symfonyのサンプルコード投稿サイト「snippets」の全ソース も
  非常に参考にさせていただいていること
  申し加えておく。感謝。

commentsコメント
comment_post












管理者にだけ表示を許可する
commentトラックバック
トラックバックURL:
http://metaboy.blog23.fc2.com/tb.php/1118-348aebdb
ようこそ
Add to Google 創るmetaboy:RSSフィード
My Yahoo!に追加
最新記事のRSS | 問い合わせ

仕事検索、アルバイト検索、依頼仕事の検索ポータル - 仕事検索.COM - www.jobkensaku.com ツクルン

創るmetaboy - WEB創る、サイト創る、何創る - 創ったmetaboy

 

リンク集

 

最近の記事

 

ブロとも申請フォーム
Sponserd by

さくらのレンタルサーバ さくらのレンタルサーバ
大容量・高機能レンタルサーバー heteml 大容量・高機能レンタルサーバー heteml
XREA (ValueDomain)
お名前.com お名前.com
名づけてねっと名づけてねっと
ムームードメインムームードメイン

 

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。