CakePHP5入門【CakePHP5実用編⑫】入室制限
A子
限られた人だけが入室できるように、改良したいんだよね
C菜
|
1.原則として誰でも入室できるけど、指定した人については例外的に入室できないようにする
2.原則としてチャットルーム作成者以外を入室できないようにしておいて、指定した人だけは例外的に入室できるようにする |
のどちらでしょうか~?
A子
B美
チャットルームの作成時や編集の際に、どちらにするかを選択できるようにして…
C菜
「chat_rooms」テーブルに「原則許可」と「原則拒否」の種別を格納する項目を追加しましょう~
A子
|
create table chat_rooms (
id int auto_increment primary key, theme varchar(255) not null, admin_id int not null, delete_flag int not null, access_policy int, created datetime, modified datetime, foreign key(admin_id) references users(id) ) charset=utf8mb4; |
C菜
|
define("ALL_ALLOW", 1); //原則許可
define("ALL_DENY", 2); //原則拒否 |
で、どうでしょうか~?
B美
それじゃ、次は「例外」を格納するテーブルね
A子
|
create table exceptions (
id int auto_increment primary key, chat_room_id int not null, user_id int not null, access_policy int, created datetime, modified datetime, foreign key(chat_room_id) references chat_rooms(id), foreign key(user_id) references users(id) ) charset=utf8mb4; |
今回は、きちんとCakePHPの命名規則に沿った名付けにしてるよ
具体的には、外部キーである「chat_room_id」と「user_id」って、「参照先のテーブル名の単数形+'_id'」にしてるからね
(【CakePHP5実用編④】を参照)
C菜
チャットルームとユーザの関係(カーディナリティ)って、「多対多」になりますもんね~
(【コラム⑩】を参照)
B美
それじゃ、さっそくデータベースの修正からやってみなさい
A子
まずは「chat_rooms」テーブルの修正からだね
| mysql -u root -p chatdb[Enter] |
|
ALTER TABLE chat_rooms
ADD access_policy int AFTER delete_flag;[Enter] |
C菜
A子
|
cd html/authapp[Enter]
bin/cake bake model exceptions[Enter] bin/cake cache clear_all[Enter] |
B美
「src/Model/Entity」の中にある「ChatRoom.php」だけど、$_accessibleの中に以下の一文を追加しておきなさいね
| 'access_policy' => true, |
C菜
B美
この記述が無いと、HTMLフォームの中に「アクセスポリシーのラジオボタン」を追加しても、それが無視されちゃうからね
(「ChatRoomsController.php」を修正しても良いんだけど、面倒くさい(苦笑))
A子
そんじゃ、まずは「config」の中にある「const.php」に定数を追加することからだね
|
define("ALL_ALLOW", 1); //原則許可
define("ALL_DENY", 2); //原則拒否 define("POLICY_NAME", ["-", "原則許可", "原則拒否"]); |
C菜
あ、「新規作成」時のデフォルト値については、「原則許可」にしてますよ~
A子
テストしてみたけど、全く問題なかったよ
B美
できるだけ自動化したい(レコード操作をCakePHPに任せたい)から、Model側のファイルを修正するのが王道のやり方なんだけど、私としてはあまりそれに頼りたくない
(というか、お勧めしない)
C菜
B美
「belongsToMany」を使いこなせる人も割といるみたいだけどね
A子
何それ?
B美
私には使いこなせないけど…(苦笑)
C菜
A子
それじゃ、どうすんの?
B美
まずは「templates/ChatRooms」の中にある「add.php」を修正してみましょう
C菜
B美
自分自身を一覧の中に表示しないってことと、チェックボックスが配列になってるってことがポイントね
(お試しユーザにマークを付けるってこともやってるけど…)
A子
まずはテーブルのフェッチからだね
|
private $Users;
private $Exceptions; public function initialize(): void { parent::initialize(); $this->Users = $this->fetchTable('Users'); $this->Exceptions = $this->fetchTable('Exceptions'); } |
んで、「add」メソッドはこんな感じでどうかな?
|
public function add()
{ $this->checkGuest(); //ユーザの一覧を取得 $users = $this->Users->find()->all(); $chatRoom = $this->ChatRooms->newEmptyEntity(); if ($this->request->is('post')) { $chatRoom = $this->ChatRooms->patchEntity($chatRoom, $this->request->getData()); if ($this->ChatRooms->save($chatRoom)) { //例外ユーザの追加 $exception_users = $this->request->getData('exception_users'); if (!empty($exception_users)) { foreach ($exception_users as $user_id) { $exception = $this->Exceptions->newEmptyEntity(); $exception->chat_room_id = $chatRoom->id; $exception->user_id = $user_id; $exception->access_policy = $chatRoom->access_policy; $this->Exceptions->save($exception); } } $this->Flash->success(__('チャットルームを作成しました。')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('チャットルームの作成に失敗しました。')); } $this->set(compact('chatRoom', 'users')); } |
配列として渡されてきたチェックボックスの値(ユーザID)を「exceptions」テーブルに格納していくだけなんだけどね
C菜
(ほとんど流用ですけど~)
ポイントはチェックボックスのチェック状態をデータベースから反映させることですね~
A子
その中にある「$exceptions」って?
C菜
| $exceptions = $this->Exceptions->find()->where(['chat_room_id' => $id])->all()->extract('user_id')->toList(); |
ポイントは「user_id」の値だけをリスト(配列)化していることですね~
A子
だったら「edit」メソッドはこんな感じになるかな?
|
public function edit($id = null)
{ $this->checkGuest(); $chatRoom = $this->ChatRooms->get($id, contain: []); //管理人でなければリダイレクト if ($chatRoom->admin_id != $this->identity->id) { return $this->redirect(['action' => 'index']); } //ユーザの一覧を取得 $users = $this->Users->find()->all(); //このチャットルームに紐づく例外ユーザを検索し、そのユーザIDのみを配列として取得する $exceptions = $this->Exceptions->find()->where(['chat_room_id' => $id])->all()->extract('user_id')->toList(); if ($this->request->is(['patch', 'post', 'put'])) { $chatRoom = $this->ChatRooms->patchEntity($chatRoom, $this->request->getData()); if ($this->ChatRooms->save($chatRoom)) { //例外ユーザの削除及び追加 $this->Exceptions->deleteAll(['chat_room_id' => $id]); $exception_users = $this->request->getData('exception_users'); if (!empty($exception_users)) { foreach ($exception_users as $user_id) { $exception = $this->Exceptions->newEmptyEntity(); $exception->chat_room_id = $id; $exception->user_id = $user_id; $exception->access_policy = $chatRoom->access_policy; $this->Exceptions->save($exception); } } $this->Flash->success(__('チャットルーム情報を更新しました。')); return $this->redirect(['action' => 'index']); } $this->Flash->error(__('チャットルーム情報の更新に失敗しました。')); } $this->set(compact('chatRoom', 'users', 'exceptions')); } |
ポイントとしては、このチャットルームの例外リストを全て削除してから、新たに挿入(新規作成)していることだね
(更新処理を行うより分かりやすいと思うんだけど…)
B美
(てか、なかなかやるわね…口には出さないけど)
それじゃ、最後は「ChatController.php」ね
この中の「index」メソッドで、チャットルームに入室できるかどうかを検証してみなさい
A子
えーっと、「exceptions」テーブルの中に、アクセスしてきたユーザの「ユーザID」があるかどうかを調べれば良いんだよね
で、「原則許可」のときにレコードが存在すればアクセス禁止にして、「原則拒否」のときに存在するならアクセスを許可すれば良いのか…
「initialize」メソッド内で「exceptions」テーブルをフェッチするのは当然として、「index」メソッド内にこういう記述を行えば良いと思う
|
$chat_room = $this->ChatRooms->find()->where(['id' => $room_id])->first();
if ($chat_room->admin_id != $this->identity->id) { //例外リストに記載があるか $ex_count = $this->Exceptions->find()->where(['chat_room_id' => $room_id, 'user_id' => $this->identity->id])->count(); if ($chat_room->access_policy == ALL_ALLOW) { //原則許可 if ($ex_count > 0) { //ログインユーザが例外リストに入っている場合、アクセス拒否 $this->Flash->error(__('このチャットルームに入室する権利がありません。')); return $this->redirect(['controller' => 'ChatRooms', 'action' => 'index']); } } else if ($chat_room->access_policy == ALL_DENY) { //原則拒否 if ($ex_count == 0) { //ログインユーザが例外リストに入っていない場合、アクセス拒否 $this->Flash->error(__('このチャットルームに入室する権利がありません。')); return $this->redirect(['controller' => 'ChatRooms', 'action' => 'index']); } } } |
えっと、2行目のif文で「作成者」は無条件で入室できるようにしてるよ
あと、4行目で「exceptions」テーブル内に、入室しようとするユーザのレコードがあるかどうかを調べてるから…
B美
C菜
ついでと言っては何ですが、「templates/ChatRooms」の中にある「index.php」と「view.php」内の表記を少しだけ変更しましょう~
具体的には「管理人」という文言を「作成者」に変更するです~
(ユーザ権限としての「管理者」と、チャットルーム作成者としての「管理人」がごっちゃになりそうなので~)
A子
さて、それじゃテストしてみますかー
まずは「チャットルーム作成」だね
・・・
うん、自分以外が例外ユーザとしてきちんと列挙されてるじゃん
(お試しユーザの右端にも「*」が付いてる)
↓
A子
(「原則許可」で例外なしってことね)
C菜
(「原則拒否」で例外なしってことです~)
↓
A子
(「原則許可」と「原則拒否」の二つね)
↓
↓
B美
私のアカウントで入室を試みてみましょう
・・・
うん、7番の部屋以外は入れたわね
C菜
B美
OK、OK
6番の部屋以外は入室拒否されたわね
(所定の性能を発揮した…ってこと)
A子
7番の部屋について、B美だけは許可してあげるよ
B美
うん、大丈夫みたい
入室できたわよ
C菜
A子
完璧じゃん!
B美
さっき「お試しユーザを拒否」って部屋を作ったわよね
あれって、「例外ユーザ」として個別にチェックを入れただけで、「お試しユーザ」全員を一斉に拒否する機能なんて無いからね
(勘違いしないように!)
A子
その機能って追加したほうが良いかな?
C菜
(個別に指定できるんですから~)
B美
私も要らないと思うけどね
まぁ、この記事を読んでいる皆さんが(必要ならば)独自に拡張すれば良いんじゃない?
A子
メタ発言(笑)


