CakePHP1.3.6:Authコンポーネントで認証する(その2)

前回のログに引き続き、usersテーブルをメンテナンスするプログラムを作成する。
今回は、hasOneの関係を試すために、user_attrsテーブルを以下のようなスキーマで作成する。

CREATE TABLE IF NOT EXISTS `user_attrs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `yomi` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `mail` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `tel` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `memo` text COLLATE utf8_unicode_ci NOT NULL,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

面倒だった点は、

  • usersテーブルのpasswordフィールドはハッシュ化されて保存されるが、未入力の場合にそれがハッシュ化されてしまい、未入力のチェックができない

こと。これには、こちらのサイトを参考にさせていただいた。感謝。
こちらのサイトで、実際にテーブルに無いフィールドに関して、モデルにvalidateを定義できることを知った。これは面白い!!と思ってやってみた。それで分かったことは、

  • save時にvalidateされてエラーが帰ってくるが、saveされてしまう。

ということ。save前にvalidate()して、戻り値を取得すれば充分使えると思った。

また、実運用上、ハッシュ化されたパスワードが保管されると、

  • パスワードを忘れたときには、再発行するしかない

ことになる。パスワードを忘れた場合に、メイルで送ってもらう、という運用があるが、管理人にもパスワードが分からないことは漏洩、なりすましの軽減になる。
Authに関しては、パスワードのハッシュ化がmustではないが、デフォルトの仕様を利用することにした。

モデル

users.php

user_attrとhasOneの関係を結び、カスケード・デリートの設定をする。

<?php 

class User extends AppModel {
	public $name = 'User';

	public $hasOne = array(
		"UserAttr" => array(
			'className' => 'UserAttr',
			'dependent' => true
		)
	);
	
	public $validate = array(
			'username'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください'),
							array(
								'rule' => 'isUnique',
								'message' =>'すでに登録されています'),
							array(
								'rule' => 'alphaNumeric',
								'message' =>'半角英数字以外は入力できません')
					),
/*			'new_password'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください'),
							array(
								'rule'     => 'alphaNumeric',
								'required' => true,
								'message'  =>'半角英数字以外は入力できません'
								),
 							array(
    							'rule' => array('between', 6, 20),
    							'required' => true,
    							'message' => 'パスワードは6〜20文字以下で入力して下さい。'
    							)						
					)
*/
	);
}

?>
user_attr.php
<?php 
require_once '../vendors/MyValidator.class.php';
require_once '../vendors/MyMBValidator.class.php';

class UserAttr extends AppModel {
	public $name = 'UserAttr';

	public $validate = array(
			'name'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください'),
							array(
								'rule' => 'isUnique',
								'message' =>'すでに登録されています'),
					),
			'yomi'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください'),
							array(
								'rule' => array('checkHiragana','yomi'),
								'message' =>'ひらがなで入力してください'),
					),
			'tel'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください')
					),
			'mail'=>array(
							array(
								'rule' => 'notEmpty',
								'message' =>'入力してください'),
							array(
								'rule' => array('checkMail','mail'),
								'message' =>'メイルの形式がただしくありません'),
					),
			);
	
	public function checkHiragana($data,$name){
		return MyMBValidator::validate('isHiragana', $data[$name]);
	}
	
	public function checkMail($data,$name){
		return MyValidator::validate('ja','isEmail', $data[$name]);
	}
	
}

?>

画面

ユーザー一覧(index.ctp)

index.ctp
<h2>ユーザーの一覧</h2>
<br>
名前で絞り込みをします。
<?php 
// 絞り込み用フォーム
echo $form->create(null,array('type'=>'post','action'=>'.'));
echo $form->text('UserAttr.name', array('size' => 20));
echo $form->end('送信');
?>

<?php 
// ページネーション
echo $paginator->numbers(
	array(
		'before'=>$paginator->hasPrev() ? $paginator->first('<<').' ' : '',
		'after'=>$paginator->hasNext() ? ' '.$paginator->last('>>') : '',
		'modulus'=>4,
		'separator'=>' '
	)
);
?>

<table>
<tr>
	<th><?php echo $paginator->sort('ID','User.id') ?></th>
	<th><?php echo $paginator->sort('ユーザーID','User.username') ?></th>
	<th><?php echo $paginator->sort('名前','UserAttr.name') ?></th>
	<th><?php echo $paginator->sort('電話','UserAttr.tel') ?></th>
	<th><?php echo $paginator->sort('更新日','User.timestamp') ?></th>
</tr>
<?php 
	foreach ($data as $arr){
		echo '<tr>';
		echo '<td>'.$html->link($arr['User']['id'],
  				array('controller'=>'users', 'action'=>'show',$arr['User']['id'])).'</td>';
		echo "<td>{$arr['User']['username']}</td>";
		echo "<td>{$arr['UserAttr']['name']}</td>";
		echo "<td>{$arr['UserAttr']['tel']}</td>";
		echo "<td>{$arr['User']['timestamp']}</td>";
		echo '</tr>';
	}
?>
</table>
<?php
echo $html->link('メニューへ戻る',
  array('controller'=>'main', 'action'=>'index'));
echo '&nbsp;';
echo $html->link('登録',
  array('controller'=>'users', 'action'=>'add'));
?>
登録

add.ctp
<h2>ユーザーの登録</h2>
<?php 
echo $form->create('User',array('action'=>'add'));
?>

<table>

<?php 
echo "<tr><th>ユーザー名</th>";
echo "<td>{$form->text('User.username',array('size'=>'30'))}{$form->error('User.username')} </td>";
echo "</tr>";
echo "<tr><th>パスワード</th>";
echo "<td>{$form->text('User.new_password',array('size'=>'30'))}{$form->error('User.new_password')}</td>";
echo "</tr>";
echo "<tr><th>名前</th>";
echo "<td>{$form->text('UserAttr.name',array('size'=>'20'))}{$form->error('UserAttr.name')}</td>";
echo "</tr>";
echo "<tr><th>よみ</th>";
echo "<td>{$form->text('UserAttr.yomi',array('size'=>'20'))}{$form->error('UserAttr.yomi')}</td>";
echo "</tr>";
echo "<tr><th>電話</th>";
echo "<td>{$form->text('UserAttr.tel',array('size'=>'20'))}{$form->error('UserAttr.tel')}</td>";
echo "</tr>";
echo "<tr><th>メイル</th>";
echo "<td>{$form->text('UserAttr.mail',array('size'=>'30'))}{$form->error('UserAttr.mail')}</td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$form->textarea('UserAttr.memo')}</td>";
echo "</tr>";
echo $form->end('登録');
?>
</table>
<a href=".">リストに戻る</a>
照会

パスワードは参照しない。

show.ctp
<h2>売上の詳細</h2>
<br>
<table>

<?php 
echo "<tr><th>商品名</th>";
echo "<td>{$data['User']['username']}</td>";
echo "</tr>";
echo "<tr><th>名前</th>";
echo "<td>{$data['UserAttr']['name']}</td>";
echo "</tr>";
echo "<tr><th>よみ</th>";
echo "<td>{$data['UserAttr']['yomi']}</td>";
echo "</tr>";
echo "<tr><th>電話</th>";
echo "<td>{$data['UserAttr']['tel']}</td>";
echo "</tr>";
echo "<tr><th>メイル</th>";
echo "<td>{$data['UserAttr']['mail']}</td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$data['UserAttr']['memo']}</td>";
echo "</tr>";
echo "<tr><th>最新更新日</th>";
echo "<td>{$data['User']['timestamp']}</td>";
echo "</tr>";
?>
</table>
<a href="../">リストに戻る</a>
&nbsp; &nbsp;
<?php
echo $html->link('更新',
  array('controller'=>'users', 'action'=>'edit', $data["User"]["id"]));
?>
更新

パスワードは表示せず(ハッシュ化されているので表示しても仕方ない)、入力があったら変更する。


edit.ctp
<h2>ユーザー情報の更新</h2>
<?php 
echo $form->create('User',array('type'=>'post','action'=>'edit'));
// UserとUserAttrのそれぞれを更新するので、hiddenで保持すること。
echo $form->hidden('User.id');
echo $form->hidden('UserAttr.id');
?>

<table>

<?php 
echo "<tr><th>ユーザー名</th>";
echo "<td>{$form->text('User.username',array('size'=>'30'))}{$form->error('User.username')} </td>";
echo "</tr>";
echo "<tr><th>パスワード</th>";
echo "<td>{$form->text('User.new_password',array('size'=>'30'))}{$form->error('User.new_password')}</td>";
echo "</tr>";
echo "<tr><th>名前</th>";
echo "<td>{$form->text('UserAttr.name',array('size'=>'20'))}{$form->error('UserAttr.name')}</td>";
echo "</tr>";
echo "<tr><th>よみ</th>";
echo "<td>{$form->text('UserAttr.yomi',array('size'=>'20'))}{$form->error('UserAttr.yomi')}</td>";
echo "</tr>";
echo "<tr><th>電話</th>";
echo "<td>{$form->text('UserAttr.tel',array('size'=>'20'))}{$form->error('UserAttr.tel')}</td>";
echo "</tr>";
echo "<tr><th>メイル</th>";
echo "<td>{$form->text('UserAttr.mail',array('size'=>'30'))}{$form->error('UserAttr.mail')}</td>";
echo "</tr>";
echo "<tr><th>メモ</th>";
echo "<td>{$form->textarea('UserAttr.memo')}</td>";
echo "</tr>";
echo $form->end('更新');
?>

</table>
<a href="../">リストに戻る</a>
&nbsp; &nbsp;
<?php
echo $html->link('削除',
  array('controller'=>'users', 'action'=>'delete', $data["User"]["id"]),
  array(),
 "削除してもいいですか?");
?>

コントローラー

/app/controllers/users_controller.php

<?php
require_once '../vendors/MyConverter.class.php';

class UsersController extends AppController{
	public $name = 'Users';
	public $uses 		= array('User','UserAttr');
	public $components = array('Auth');
	public $layout 	= 'myznala';
	
	/*
	 * Paginatorの定義
	 */
	public $paginate = array(
		'page'=>1,
		'conditions'=>array(),
		'fields'=>array(),
		'order'=>array('User.timestamp'=>'desc'),
		'limit'=>5,
		'recursive'=>1
	);
	
	
	function beforeFilter(){
		$this->Auth->allow('add');
		$this->Auth->allow('logout');
		$this->Auth->authError= 'ログインしてください';
		$this->Auth->loginError = 'ログインに失敗しました';
	}
	
	/**
	 * 認証後の処理
	 * 
	 * セッションにユーザー名を保管する。
	 */
/*	function isAuthorized(){
		$this->Session->write('Login.username',$this->Auth->user('username'));
		echo $this->Auth->user('username');
	}
*/
	
	/**
	 * 
	 * ログイン
	 */
	function login(){
		$this->set('title_for_layout', "ログイン");
	}
	
	/**
	 * 
	 * ログアウト
	 */
	function logout(){
		$this->set('title_for_layout', "ログアウト");
		$this->Session->destroy();
		$this->Auth->logout();
	}
	
	/**
	 * 
	 * 初期画面(一覧表示)
	 */
	function index(){
		$this->set('title_for_layout', "ユーザーの一覧");
		$req=null;
		if(!empty($this->data)){
			//サニタイズ
			$req = MyConverter::getRequestParams($this->data["UserAttr"]);
			//絞り込みの場合には、コンディションを書き換える。
			$this->paginate['conditions'] = array('UserAttr.name like ?' => array("%{$req["name"]}%"));
		}
		$data = $this->paginate();
		
		$this->set('data',$data);
	}
	
	/**
	 * ユーザーの登録
	 */
	function add(){
		$this->set('title_for_layout', "ユーザーの登録");
		if(!empty($this->data)){
			// サニタイズ
			$this->data["User"] 		= MyConverter::getRequestParams($this->data["User"]);
			$this->data["UserAttr"] 	= MyConverter::getRequestParams($this->data["UserAttr"]);
			if ($this -> data["User"]["new_password"]) {
    			$this -> data["User"]["password"] 
    				= $this -> Auth -> password($this -> data['User']['new_password']);
				$this->User->saveAll($this->data);
				if($this->User->validates() && $this->UserAttr->validates()){
					// 登録できたらリダイレクト
					$this->redirect('.');
				}
			} else {
				$this->User->invalidate('new_password','入力してください');
  			}
		}
	}

	
	/**
	 * 
	 * データの照会
	 * $paramsにIdが入ってくる。
	 * 
	 */
	function show($param){
		$this->set('title_for_layout', "ユーザー情報の詳細");
		$result=null;
		if(!empty($param)){
			// Idフィールドによる検索
			$this->data=$this->User->findById($param);
			if(!$this->data){
				// 存在しないIDを参照された場合。
				$this->Session->setFlash('存在しません');
				$this->redirect('.');
			}
		}
		$this->set('data',$this->data);
	}
	
	/**
	 * 
	 * データの更新
	 * (注記) パスワードは表示しない。入力があったら変更する。
	 * 
	 */
	function edit($param){
		$this->set('title_for_layout', "ユーザー情報の更新");
		// データの更新
		if(!empty($this->data)){
			// サニタイズ
			$this->data["User"] = MyConverter::getRequestParams($this->data["User"]);
			$this->data["UserAttr"] = MyConverter::getRequestParams($this->data["UserAttr"]);
			// Idフィールドによる検索
			$check=$this->User->findById($param);
			if(!$check){
				// 存在しないIDを更新しようとした場合。
				$this->Session->setFlash('存在しません');
				$this->redirect('.');
			}else{
				// パスワードは入力があったら変更
				if ($this -> data["User"]["new_password"]) {
    				$this -> data["User"]["password"] 
    					= $this -> Auth -> password($this -> data['User']['new_password']);
				}
				// 更新
				$this->User->saveAll($this->data);
				if($this->User->validates() && $this->UserAttr->validates()){
					// 更新できたらリダイレクト
					$this->redirect('.');
				}
			}
		// 更新データのセット(初期値)
		}else{
			if(!empty($param)){
				// Idフィールドによる検索
				$check=$this->User->findById($param);
				if(!$check){
					// 存在しないIDを更新しようとした場合。
					$this->Session->setFlash('存在しません');
					$this->redirect('.');
				}else{
					// Idフィールドによる検索
					$this->data=$this->User->findById($param);
				}
			}
		}
		// エラーの場合も通るようにする
		$this->set('data',$this->data);
	}
	
	/**
	 * 
	 * データの削除
	 * 
	 */
	function delete($param){
		if(!empty($param)){
			$check=$this->User->findById($param);
			if(!$check){
				// 存在しないIDを削除しようとした場合。
				$this->Session->setFlash('存在しません');
			}else{
				// Idフィールドによる削除
				$this->data=$this->User->delete($param);
			}
			$this->redirect('.');
		}
	}
	

}
?>