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子
メタ発言(笑)