先週のブックマークチュートリアルの続き。先回は Tag 検索機能を実装するところまで進めた。
今回は、ブックマークチュートリアルのPart2 ユーザ認証、ログイン画面の作成に取り掛かる。
第10回・第11回の演習でトラブル等で、今回の作業を進められない受講生は↓を参照。
前回の作業内容を完了した例をレポートフォルダに入れておく。CakePHPの作業が完了していない受講生は、こちらを利用して今回の作業を進めること。bookmarkerデータベースの作成と、php.ini の設定変更は 第10回の資料を参考に進める。
php.ini の設定変更後はWebサーバの再起動が必要。
bookmarks users tags に動作確認用のデータを数件登録しておく。
作業を開始する前に、
http://localhost/bookmarker/users/ にアクセスして、登録済みのメールアドレスとパスワードを確認する。
パスワードがハッシュ化されている場合、先回設定したパスワードを忘れている場合は、パスワードを
編集して設定しなおす。
作業手順は、↓のリンク先を参照のこと。
https://book.cakephp.org/3.0/ja/tutorials-and-examples/bookmarks/part-two.html
以下に、演習室PCでの作業の要点・概略を記す。
赤字の部分を追加
// src/Controller/AppController.php の中でnamespace App\Controller;use Cake\Controller\Controller;class AppController extends Controller{ public function initialize() { $this->loadComponent('Flash'); $this->loadComponent('Auth', [ 'authenticate' => [ 'Form' => [ 'fields' => [ 'username' => 'email', 'password' => 'password' ] ] ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login' ], 'unauthorizedRedirect' => $this->referer() // 未認証時、元のページを返します。 ]); // PagesController が動作し続けるように // display アクションを許可 $this->Auth->allow(['display']); }http://localhost/bookmarker/bookmarks/
login() がないのでエラーが表示される
赤字の部分を追加
// src/Controller/UsersController.php の中でpublic function login(){ if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('あなたのユーザー名またはパスワードが不正です。'); }}http://localhost/bookmarker/bookmarks/
login.ctp がないのでエラーが表示される
src/Template/Users/login.ctp を新規作成(Usersフォルダを 右クリック→新しいファイル)。
以下を記述。
<?= の記号は、PHP のコード省略記法。 <?php echo $変数名 ?>
<h1>Login</h1><?= $this->Form->create() ?><?= $this->Form->control('email') ?><?= $this->Form->control('password') ?><?= $this->Form->button('Login') ?><?= $this->Form->end() ?>http://localhost/bookmarker/bookmarks/
表示されたページのソースコードをブラウザで確認。
UsersController に以下のコードを追加。
public function initialize(){ parent::initialize(); $this->Auth->allow(['logout']);}public function logout(){ $this->Flash->success('ログアウトします。'); return $this->redirect($this->Auth->logout());}http://localhost/bookmarker/users/logout
ログイン画面に戻る。
ユーザ登録URLにアクセスする。
http://localhost/bookmarker/users/add
ログインページに切り替わり、ユーザ登録が出来ない状態になる。
UsersController を修正。
public function initialize(){ parent::initialize(); // 許可するアクション一覧に add を追加 $this->Auth->allow(['logout', 'add']);}http://localhost/bookmarker/users/add
ユーザ登録が出来るようになっている。test用のユーザを追加で登録する。
e-mail test@nagoya-bunri.ac.jp
password test
Users/add.ctp にある、ユーザ一覧表示 List Users へのリンクはユーザ登録時にはアクセスできないので消してもよい。
ログアウト用のURL http://localhost/bookmarker/users/logout にアクセスして、ログアウトする。
先ほど登録した test ユーザでログインしなおす。
AppController に以下を追加します。
public function isAuthorized($user){ return false;}AppController のinitialize() メソッドを修正。
public function initialize(){ $this->loadComponent('Flash'); $this->loadComponent('Auth', [ 'authorize'=> 'Controller',//この行を追加 'authenticate' => [ 'Form' => [ 'fields' => [ 'username' => 'email', 'password' => 'password' ] ] ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login' ], 'unauthorizedRedirect' => $this->referer() ]); // PagesController が動作し続けるように // display アクションを許可 $this->Auth->allow(['display']);}http://localhost/bookmarker/bookmarks
↑のコード修正で全機能 bookmarksコントローラの index view add delete などへのアクセスを一律に禁止したのでログイン画面に戻される。
BookmarksControllerに以下を追加
public function isAuthorized($user){ $action = $this->request->getParam('action'); // add と index アクションは常に許可します。 if (in_array($action, ['index', 'add', 'tags'])) { return true; } // その他のすべてのアクションは、id を必要とします。 if (!$this->request->getParam('pass.0')) { return false; } // ブックマークが現在のユーザーに属するかどうかをチェック $id = $this->request->getParam('pass.0'); $bookmark = $this->Bookmarks->get($id); if ($bookmark->user_id == $user['id']) { return true; } return parent::isAuthorized($user);}CakePHP のコードについて:
$this->request->getParam('action') は、サーバにアクセスするURLのアクション部分(コントローラーの下の階層)を読み取る。アクションには、コントローラのphpコードの function が対応する。
$this->request->getParam('pass') は、URLのアクションの下のパスセグメント( / で区切ったキーワード)を数字をインデックスとする配列で呼べるようにする。'pass.0' とインデックスを指定すると配列の先頭要素、つまりパスセグメントの先頭要素を取り出せる。
↑のコードでは、index add tagsのアクション以外の全てのアクション、view や add などは、URLにidが含まれているかチェックしている。
if ( $bookmark->user_id == $user['id'] の部分で
edit や delete など 操作にユーザ id が必要で、一律に許可がされていない操作(addやindexは許可済み)の
権限チェックをする。
bookmarks のユーザid と ログインユーザのid が一致 → 操作許可 自分のブックマークは操作できる
bookmarks のユーザid と ログインユーザのid が不一致 → 操作不許可 他人のブックマークは操作できない
http://localhost/bookmarker/bookmarks
↑のページから、ブックマークの 一覧表示 と 追加 が出来るようになる。他人のidによるブックマークの編集や削除は不能で、自分のidによるブックマークは編集・削除可能。
6月28日(水)の授業はここまで進んだ。
時間に余裕がない場合、ここまでの内容で課題提出としてOK
↓この作業は不要
// src/Template/Layout/default.ctp の中の// 既存のフラッシュメッセージの下で<?= $this->Flash->render() ?>src/Template/Bookmarks/add.ctp から1行削除
echo $this->Form->control('user_id', ['options' => $users]);src/Controller/BookmarksController.php の add() アクションを修正。
public function add() { $bookmark = $this->Bookmarks->newEntity(); if ($this->request->is('post')) { $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData()); $bookmark->user_id = $this->Auth->user('id'); // この行を追加 if ($this->Bookmarks->save($bookmark)) { $this->Flash->success(__('The bookmark has been saved.')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('The bookmark could not be saved. Please, try again.')); } $users = $this->Bookmarks->Users->find('list', ['limit' => 200]); //この行を削除 $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); // usersを削除 $this->set('_serialize', ['bookmark']); }http://localhost/bookmarker/bookmarks/add
で動作確認。
ログイン後、ユーザはブックマーク登録の際に、自分のユーザidを指定しなくても登録できるようになった。
自分のユーザidを使って登録するためのコード $bookmark->user_id = $this->Auth->user('id'); の効果。
以下、Webページを参考に作業を続ける。
課題提出
bookmarker のWebサービスの画面で、
・ログイン用画面
・ログイン後の画面 ※ ユーザー認証の状況が分かるように、他のユーザのブックマークを編集したり、削除を試みる。→ 認証エラー が表示される。 この状態でスクリーンショットを撮っておく。
↑の2つの画像を、ワープロファイルに貼り付け、ファイルをWebclassの 第C回課題 にアップロードする。
例)
ログアウト後に表示されるログイン画面
他のユーザのブックマークを編集しようとして、認証エラーが出ている画面
応用
Template の 各種 ctp ファイルを編集:
・ログアウト用のリンクを表示する
・ユーザ登録画面の List Users のリンクなど、不要なリンクを削除する
・ログイン状態の各種画面に、ログインユーザーのIDを表示するようにする。$this->Auth->user('id') を利用。
ルーティングの変更、トップページのURLを設定:
・config/routes.php の / の scope を編集し、 bookmarker/ にアクセスすると bookmarksコントローラの index アクションを実行するように設定する。
先回作成した、 tag検索画面を利用するための 画面を作成:
・検索tag入力用の画面を login.ctp と同様の要領で作成する。
検索tagを文字で入力するFormを作成し、Template/Bookmarksに配置する。
入力されたtagは、Bookmarksコントローラのtagsアクションでpostリクエストで処理されるようにコードを修正する。
など、Webサービスとしてページの体裁を整えてみる。
↑
上記の応用例を実装した結果を、レポートフォルダに、
bookmarker_20170630BookmarkTutorialComplete_Custom
として入れておいたので参考にしてよい。
作業例)
Template/Layouts/default.ctp を修正、Title を修正。
Template/Layouts/default.ctp を修正、API Documents などへのリンクを削除。
Template/Bookmarks/index.ctp を修正、左サイドメニューから、ユーザ登録やタグ登録のリンクを削除。
Template/Bookmarks/index.ctp を修正、左サイドメニューに、[Tag Search] と [Logout]のリンクを追加。
Template/Bookmarks/index.ctp を修正、ブックマークリスト表示の項目から、User id を削除。
config/app.php を修正、デバッグモードをオフにして、デバッグ用のアイコンを非表示。
config/routes.php を修正、 http://localhost/bookmarker/ をサイトのトップページに設定。( / へのアクセスを bookmarsコントローラーの index アクションにルート設定)
Template/Bookmarks/tags.ctp を修正、トップページに戻るリンクを配置。
Template/Users/login.ctp を修正、新規ユーザ登録ページへのリンクを配置。
タグ検索用の画面を作成。(レポートフォルダに置いたコードは、複数タグの検索に非対応)
bookmarksコントローラに search アクションを作成。
search.ctp を作成。
検索タグ入力フォームを作成。
検索ボタンを押すと bookmarks の tags を呼ぶように設定する。
bookmarks の tags を編集し、 post リクエスト時は getData()で入力されたタグを処理するように変更。
bookmarkチュートリアル part2の作業を完了すると↓のように、ブックマーク登録画面が変更される。
ブックマークの登録時には、ユーザIDの選択は不要。タグ入力は、選択式から、テキスト入力式に変更。
複数のタグは , 区切りで入力する。
データベースには変更は無い。(タグ指定欄が繰り返し項目になっているが、そこはコードで対応)
関連タグの修正: コードでブックマークに関連した複数のタグを検索し、CSV形式に文字列処理して表示している。
関連タグの設定: CSV形式の要素を1件ずつ登録処理する。既存のタグが指定されていた場合は、Tagsテーブルでそのidを調べて設定。
新規タグの場合は、Tagsテーブルに新規レコードとして設定し、そのidをブックマークの関連テーブルに記録。
データの登録・修正・削除をPHPで実装する。
キーワード: CRUD HTMLフォーム
Blogの記事に対して、コメントを付けることができるようになったら完成。
準備: 次の2点は設定済み。もし設定が抜けていたら、設定しておく。
■デバッグモード を 開発中は ONにしておくことを推奨。 Config/core.php の記述で、Level 0 -> Level 2 に戻す。
■文字化けの対応:
CakePHPの設定ファイル編集:
D:\XAMPP\htdocs\blog学籍番号\app\Config の database.php
class DATABASE_CONFIG {public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => '192.168.111.??',
'login' => 'cakephp',
'password' => 'cakephp',
'database' => 'blog',
'prefix' => '',
'encoding' => 'utf8',
);
上記の様に、文字コードを utf8 に設定する。
■前回の内容:
プログラムの入力やファイル名、保存場所等のミスを修正する。先回の講義資料に、プログラムが掲載されているので参照のこと。
■ 記事の編集機能 と ビュー
PostsController.php に以下を追加。
public function edit($id = null) {
$this->Post->id = $id;
if($this->request->is('get')){
$this->request->data = $this->Post->read();
} else {
if($this->Post->save($this->request->data)) {
$this->Session->setFlash('Success!');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('failed!');
}
}
}
edit.ctp を作成。
<h2>Edit Post</h2><?phpecho $this->Form->create('Post',array('action'=>'edit'));echo $this->Form->input('title');echo $this->Form->input('body',array('rows'=>3));echo $this->Form->end('保存');index.ctp の記事一覧表示のループに、編集用のリンクを追加
※↓このコードでは、記事タイトルのリンクと 編集 リンクがつながって分かりにくいので、適当に隙間を空けるなどで対応してみる。
<?php echo $this->Html->link('編集','/posts/edit/'.$post['Post']['id']); ?>編集の動作確認。
編集ページ
■ 記事の削除
PostsController.php
public function delete($id = null) {
if($this->request->is('get')){
throw new MethodNotAllowedException();
}
if($this->Post->delete($id)) {
$this->Session->setFlash('Deleted!');
$this->redirect(array('action' => 'index'));
}
}
index.ctp の記事一覧表示のループに、削除用のリンクを追加
<?php echo $this->Form->postlink('削除','/posts/delete/'.$post['Post']['id'],array('confirm'=>'削除しますか?')); ?>動作確認
削除メッセージの表示例
■ コメント機能
blog データベースに commnets テーブルを追加。
create table comments ( id int not null auto_increment primary key, commenter varchar(255), body text, post_id int not null, created datetime default null, modified datetime default null);コメントテーブルにデータを追加。postsテーブルに存在する idを、post_id と指定すること。※cakePHPの命名規約に従う。テーブル名は複数形、フィールド名は単数形。
■ アソシエーションの設定
posts テーブルと comments テーブルの関係を設定する。
1つの記事に複数のコメントが付くので、1対多の関係になっている。これを Model に記述を加え設定する。
Comment.php
<?phpclass Comment extends AppModel {public $belongsTo = 'Post';
}?>Post.php
<?phpclass Post extends AppModel {public $hasMany = 'Comment';
}?>view.ctp に以下を追加
<h2>コメント</h2><ul><?php foreach ($post['Comment'] as $comment): ?><li><?php echo h($comment['body']) ?> by <?php echo h($comment['commenter']); ?></li><?php endforeach; ?></ul>↑のコードの解説
view.ctp のコードはPostsController.php の view メソッドでPostモデルの検索結果の表示に使用されています。
【PostsController.phpの一部再掲】
public function view($id = null) {
$this->Post->id = $id;
$this->set('post', $this->Post->read());
}
↑で、URLの末尾のidの記事を検索した結果が変数 $post に代入された状態で、view.ctp が動きます。
$post は配列で、
$post['Post']['title'] や $post['Post']['body']
で、検索結果の記事の タイトル(title)と 本文(body) を参照できます。
Postモデルにアソシエーション、hasMany を設定したことにより、
$post['Comment']
に、commentsテーブルのpost_idフィールド と postsテーブルのid フィールドの値が一致するすべてのコメントが代入されます。
$post は配列であり、その要素、 $postt['Comment']も配列です。(配列の中に配列が入っていいる)
最終的に、$postt['Comment']の中の各コメントを foreach でループ処理して表示しています。
cakePHP2系 を授業で扱っています。
cakePHP3系では仕様が変更されているので、自分で調べたコードを適用する際は要注意。
※ CakePHPのアソシエーションについては公式サイトの説明を参照。
http://localhost/blog学籍番号/ からBlogの記事を適当に選んでコメントが表示されるか確認する。
※↑この時点で、サンプルコメントをデータベースに書き込んでいないので、コメントは表示されない。
■ コメントの追加
CommentsController.php
<?phpclass CommentsController extends AppController {public $helpers = array('Html', 'Form');
public function add() {
if($this->request->is('post')){
if($this->Comment->save($this->request->data)) {
$this->Session->setFlash('Success!');
$this->redirect('/posts/view/'.$this->data['Comment']['post_id']);
} else {
$this->Session->setFlash('failed!');
}
}
}
}view.ctp にコメント投稿フォームを追加する。
<h2>コメント追加</h2><?phpecho $this->Form->create('Comment', array('action' => 'add'));echo $this->Form->input('commenter');echo $this->Form->input('body', array('rows' =>3));echo $this->Form->input('Comment.post_id', array('type'=>'hidden', 'value'=>$post['Post']['id']));echo $this->Form->end('コメント投稿');コメントの投稿と表示を確認する。
■ コメントの削除
CommentsController.php にコメント削除機能を追加する。
public function delete($id = null) {
if($this->request->is('get')){
throw new MethodNotAllowedException();
}
if($this->Comment->delete($id)) {
$this->Session->setFlash('Deleted!');
$this->redirect('/posts/index');
}
}
view.ctp のコメント表示のループの中に、各コメントごとの 削除リンクを追加
<?php echo $this->Form->postlink('削除','/comments/delete/'.$comment['id'],array('confirm'=>'削除しますか?')); ?>動作確認
データベース設計演習1/4 ~ 3/4 について、以下の手順で cakePHP のファイルと blog データベースを
学籍番号付のフォルダやファイルとして、レポートフォルダへ提出する。
・ cakePHP : d:\xampp\htdocs\blog学籍番号 を レポートフォルダ(データベース)の提出場所へ提出
・ blog データベース : phpMyAdmin から blogデータベースを選択して エクスポート する。
ファイル名を blog3113999.sql に変更してレポートフォルダ(データベース)の提出場所へ提出
期末課題として作成する、Webサービスのテーマが決まっていないものは、決めておく。
CakePHP で制作するために必要な項目を決めておく。
・データベース名
・テーブル名(1~3個でよい)
・フィールド名 と 型(各テーブルについて)
例1)
テーマ: ボードゲーム貸し出し管理
データベース名: boardgame
テーブル名とフィールド名
games
id int auto_incremental primary key
name varchar(20) not null
desc text
users
id int auto_incremental primary key
name varchar(20) not null
rentals
id int auto_incremental primary key
user_id not null
game_id not null
date datetime
例2)
テーマ: ゲームカタログ
データベース名: gamecatalog
games
id int auto_incremental primary key
name varchar(20) not null
desc text
genre_id
genres
id int auto_incremental primary key
name varchar(20) not null
■期末課題の提出物
blog を作成したと同様に、データベース名のフォルダを作成し、CakePHPを準備する。
・学籍番号+データベース名 のフォルダ
・学籍番号+データベース名.sql のエクスポートファイル
の2点を期日までに提出する。(〆切は7月末の予定)
■期末課題採点基準:(30点満点+)
・データベースの設計 正規化 ・ テーブル間の関係 (20点)
・サンプルデータ (5点)
・独自の工夫(カスタマイズビューの作成・操作メニューの作成・Webリンク・並べ替え・集計機能・セキュリティなど)(5点~)
ビューとコントローラーは、 scaffold で単純に作成してもよい。
テーブルが1つしかないデータベースの制作でもかまわないが、複数テーブルのデータベースに対して、評価を半減する。
データベースの構造で減点されない分を、独自の工夫でカバーするように。