2.開発の実際
2-1.ログイン
2-1-1.ログイン画面を作る
2-1-2.日本語にしてみる
2-1-3.ログインの準備
2-1-4.modelsのファイル
2-1-5.ログイン認証
2.開発の実際
2-1.ログイン
2-1-1.ログイン画面を作る
開発現場では、公開ホームページだけではなく、必ずと言っていいほど、管理用ホームページが必要になります。
Yii Frameworkでは、デフォルトでログイン機能がありますので、これをそのまま利用させていただきます。
できあがりを、以下のようなディレクトリ構造とします。
つまり、管理機能をmanagerディレクトリに収めます。
システムで共用できるcssとyiiはmanagerの外に出しておきましょう。
ということで、managerディレクトリからのcssとyiiを見るパスのあり方を変えなくてはなりません。
manager直下のindex.phpのyiiパスを相対パスに変更します。
$yii='../yii/framework/yii.php';
さらに、manager/views/layouts直下のmain.phpのcssパスを絶対パスにします。つまり、<?php echo Yii::app()->request->baseUrl; ?>をすべて削除するだけです。
また、ログイン画面でパンくずもメニュー・タブも必要ありませんから、見えなくさせます。
見えなくさせるということは、ログイン状態ではないという意味ですので、ログイン状態を何が判断しているかというと、セッション情報がGUESTかそうでないかということになっています。GUESTであれば、未ログイン状態ということになります。
それは、Yii::app()->user->isGuestが是か非かということで表されます。
まず、ログイン用のビュー、manager/protected/views/site直下のindex.phpの頭の以下の部分を削除します。パンくずは、この部分で作っています。
<?php
$this->pageTitle=Yii::app()->name . ' - Index';
$this->breadcrumbs=array(
'Index',
);
?>
manager/views/layouts直下のmain.phpの該当部分を、下記のようにして、ログイン状態のときだけ表示するようにします。
<?php if(!Yii::app()->user->isGuest) ?>
<div id="header">
<div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div>
</div><!-- header -->
<div id="mainmenu">
<?php $this->widget('zii.widgets.CMenu',array(
'items'=>array(
array('label'=>'Home', 'url'=>array('/site/index')),
array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')),
array('label'=>'Contact', 'url'=>array('/site/contact')),
array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),
array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)
),
)); ?>
</div><!-- mainmenu -->
<?php $this->widget('zii.widgets.CBreadcrumbs', array(
'links'=>$this->breadcrumbs,
)); ?><!-- breadcrumbs -->
<?php } ?>
headerクラス部分がロゴ(『My Web Application』がデフォルト)、mainmenuクラス部分がメニュー・タブ、widgetがパンくずの部分になります。
http://localhost/manager/
で見てください。下図のようになるはずです。
2-1-2.日本語にしてみる
せっかくできた画面ですが、デザインはともかく、英語ばかりです。これを日本語にします。
Login:これは問題ないですね。
<h1>Login</h1>
↓
<h1>ログイン</h1>
にすれば、この部分は日本語になります。
Please fill out the following form with your login credentials:も、そのまま
<p>Please fill out the following form with your login credentials:</p>
↓
<p>IDとパスワードを入力してください</p>
と,日本人向けに変えるだけですね(直訳すると、『下記フォームをあなたのログイン認証情報で埋めてください』)。
Fields with * are required.についても、
<p class="note">Fields with <span class="required">*</span> are required.</p>
↓
<p class="note"><span class="required">*</span>印は必須です。</p>
としましょう。
さて、Username、Password、それにRemember me next timeはどうすればいいのでしょうか。
どれもラベルの内容です。この場合、
CHtml::activeLabelEx($model,'Username')
CHtml::activeLabelEx($model,'Password')
CHtml::activeLabel($model,'Remember me next time')
の'で囲まれた部分を日本語にします。
ただし、Usernameというところ。日本では、名前をそのまま使うのは文字ばけ問題から慣習的になくて、半角のニックネームかニックネーム的な名前をIDと称して使うのが普通です。したがって、ここでいうUsernameも、ラベルは「ID」、変数は「userid」としましょう。
<?php echo CHtml::activeLabelEx($model,'ID'); ?>
<?php echo CHtml::activeTextField($model,'userid'); ?>
ほかは、簡単に日本語にできますよね。ただし、ヒントは実際の画面に表示するわけがないので、削除してしまっています。結果は、下図のようになります。
2-1-3.ログインの準備
ログインするとなると、ログインユーザーのテーブルがデータベースに必要になります。
テーブルは、1.開発環境で触れたdataディレクトリに注目します。この配下にschema.mysql.sqlというファイルがあります。ここに、テーブル情報を書き込みます。
テーブル情報と言っても、CREATE文と、テスト用あるいは実際に入れた、あるいは入れられたデータをINSERT文化した内容です。この時点で必ずしもINSERT文はなくてもだいじょうぶです。とりあえずCREATE文を用意してください。
CREATE TABLE tbl_user (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
userid VARCHAR(128) NOT NULL,
password VARCHAR(128) NOT NULL,
username VARCHAR(128) NOT NULL,
email VARCHAR(128) NOT NULL,
blog VARCHAR(128) NOT NULL,
photo VARCHAR(128) NOT NULL,
status CHAR(1) NOT NULL,
created datetime NOT NULL,
modified datetime NOT NULL
);
idとuseridが紛らわしいですが、idは整理番号、useridが、いわゆるIDで、任意で設定される識別名です。idは特にいらないのですが、データベース内の管理キーとして利用します。
それから、1.開発環境で使ったyiicコマンドに再び登場してもらいます。
ここで注意すべきは、このCREATE文を使って実際にデータベースに前もってテーブルを作っておくことです。そうしないと、ワーニングが発せられ、テーブルの枠だけのmodelsができてしまいます。
テーブルには、開発用のIDとパスワードをINSERTしておいてください。ID=demo、パスワード=demoとしておきましょう。
Symfonyあたりだとテーブルも作ってくれてしまいますが、Yiiにはないです。また、文字コーディングはUTF-8であることも確認してください。あとで文字ばけ問題を避けるため、UTF-8で統一します。
それから、yiicコマンドを使います。
まず、コマンドのあるディレクトリに移動して・・・
C:\xampp\htdocs\yii\framework>yiic webapp c:xampp\htdocs
そして、次のようにシェルを動かします。
C:\xampp\htdocs\yii\framework>yiic shell c:\xampp\htdocs\manager\index.php
すると、
Yii Interactive Tool v1.1 (based on Yii v1.1.0)
Please type 'help' for help. Type 'exit' to quit.
>>
と、入力を促されますので、次のコマンドを入力します。
>> model Tbl_user
これにより、modelsに、テーブル名のファイルが作られます。テーブルの仕様が反映されています。ただし、フォームとの関連では、このままでは使えません。言うなればオブジェクト・ファイル化することになります。
2-1-4.modelsのファイル
SiteController.phpのactionIndexでは、IndexFormを使うようにしていますので、このTbl_user.phpをIndexForm.phpに変更し、中身のクラス名と継承するスーパークラスもフォーム用に、class IndexForm extends CFormModelとします。
さらに、ファイルの頭でコメントされているオブジェクトをオブジェクトとして使えるように修正し、不要なオブジェクトは削除、ここにはない「Idを記憶させる」変数rememberMeを加えます。
こうなるはずです。
class IndexForm extends CFormModel
{
public $userid;
public $password;
public $rememberMe;
次にくるmodelメソッドが、テーブルとのやりとりをするところです。たとえば、このtbl_userテーブルを読み込む場合、$models=Tbl_user::model()->findAllBySql($sql); のように使います。この『YiiFramework研究』ではSQLをそのまま動作させるので、ほとんど使うこともないですが。
フォームに関係なくアクセスするためにだけテーブルを使う場合には、同じTbl_user.phpの内容は、以下のようにmodelメソッドまでの簡素なもので足ります。
class Tbl_user extends CActiveRecord
{
/**
* The followings are the available columns in table 'tbl_user':
*/
/**
* Returns the static model of the specified AR class.
* @return CActiveRecord the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}
次のtableNameメソッドは、実際のテーブル名を返すだけのところですね。特に必要ないです。
それから、rulesメソッド。バリデーションの設定をここで行います。
Yii公式ガイドの『モデルの作成』にバリデーションの種類がありますので、必要に応じて探してください。
この例で不足しているのは、「Idを記憶させる」変数rememberMeの真偽判定と、パスワードが入れられた場合の処理メソッドを設定します。
不要な部分を消して、以下のような感じになります。
public function rules()
{
return array(
// userid, passwordは、必須です。
array('userid, password', 'required'),
// userid, passwordの最大文字長は128桁です。
array('userid, password', 'length', 'max'=>128),
// rememberMeは真か偽です。
array('rememberMe', 'boolean'),
// password は、authenticateメソッドの判定を必要とします。
array('password', 'authenticate'),
);
}
そうそう。このままでは、バリデーションで出力されるエラーは英語です。これを日本語にするには、config配下main.phpの最初に、
'language'=>'ja',
を挿入してください。バリデーション・エラーが日本語で表示されるようになります。
パスワードは、authenticateというメソッドを必要としたので、そのメソッドをクラスに加えます。これも、もともとのIndexForm.phpにあるので、それをいただきます。
ログインフォームでUsernameは、IDとuseridに変更しましたので、それに合わせて変更し、エラーの表示内容も日本語にします。
public function authenticate($attribute,$params)
{
if(!$this->hasErrors())
{
$identity=new UserIdentity($this->userid,$this->password);
$identity->authenticate();
switch($identity->errorCode)
{
case UserIdentity::ERROR_NONE:
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 日
Yii::app()->user->index($identity,$duration);
break;
case UserIdentity::ERROR_USERNAME_INVALID:
$this->addError('userid','IDが不正です。');
break;
default: // UserIdentity::ERROR_PASSWORD_INVALID
$this->addError('password','パスワードが不正です。');
break;
}
}
}
次のrelationsメソッド。標準で他のモデルと連動する機能ということですが、この作例では不要です。SQLを直接動作させる予定なので。
attributeLabelsメソッドは、そのオブジェクトを画面表示する際の表示内容を設定するところです。ログインフォームではフォームに直接記述したので、ここでは不要です。また別のフォームのところで触れることになります。
2-1-5.ログイン認証
バリデーションで、パスワードはauthenticateメソッドを使うことになり、さらにauthenticateメソッドは、UserIdentity.phpをインスタンス化しています。実際の認証は、このUserIdentity.phpで行われるわけです。
デフォルトでは、$usersにIDとパスワードをそれぞれ、demo/demo、admin/adminと設定しています。この$usersに、tbl_userテーブルから読み込んだIDとパスワードを入れればいいわけです。
デフォルト仕様を利用するため、Yiiシステムを少々いじることになります。UserIdentity.phpが継承しているCUserIdentityを手直ししなければなりません。yii/framework/web/auth配下にあります。
usernameという項目名を、すべてuseridに変更します。
さらに、デフォルトでは認証はsite/loginになっています。コントローラーのsiteはいいとして、loginファイルをindexに変えたので、その部分を変更します。yii/framework/web/auth配下のCWebUserファイルのloginをすべてindexに変えます。
さて、いよいよ、認証用のデータ、$usersを設定しましょう。
まず、tbl_userテーブルからデータを取得しなければなりません。
冒頭で述べたように、当作例では、SQLを直に使う仕様で考えています。これには、2通りあります。modelsに用意したテーブル・ファイルを使う方法と、その場その場でデータベースに直接アクセスする方法です。
まず、modelsのテーブル・ファイルのクラス(上述Tbl_userクラス)を使う場合ですが、このようになります。
// SQL文を構築します。
$sql='select userid, password from tbl_user ';
$sql.="where userid='".$this->userid."' ";
// SQL文を実行します。
$models=Tbl_user::model()->findBySql($sql);
テーブルはtbl_user、クラスはTbl_userです。大文字小文字の使い分けに気をつけてください。
findBySqlは該当の1行だけを読み込みます。特定のデータではなく全件の場合は、findAllBySqlになります。
もう一つはその場その場でデータベースにアクセスする方法ですが、データベースへの接続から読み込みまで設定する必要があります。めんどうそうですが、形が決まっているので、一度知れば、あとはコピー&ペーストの世界ですね。こんな感じです。
// データベースに接続します
$connection = CActiveRecord::getDbConnection();
// SQL文を構築します。
$sql='select userid, password from tbl_user ';
$sql.="where userid='".$this->userid."' ";
// SQL文を実行します。
$command=$connection->createCommand($sql);
$models=$command->query();
さっきより2行増えました。特定の1行はquery、そうでなく全件の場合はqueryAllを使います。
どちらを使うかはお好みです。modelsにテーブルごとのファイルを置くか置かないかの違いでもあります。手抜きの好きな筆者は、後者を使います。
後者を使い、検索結果の$modelsから必要なデータを$usersに、IDを連想配列キーとしたパスワードの配列を作りこめばいいわけです。
こんなふうになればいいわけです。
$users=array();
foreach($models as $key=>$value){
$users=array(
// username => password
$value['userid']=>$value['password'],
);
}
|