CakePHP5入門【CakePHP5実用編⑥】会話の画面表示

B美
まずはトップページにチャットルーム(一覧)へのリンクを作ろうか

C菜
「templates/Top」の中にある「index.php」をこのように書き換えてみました~


A子

↓


B美
それじゃ、次は「templates/ChatRooms」の中にある「index.php」よ
「ChatController」の「index」メソッドへのリンクを作るんだけど、チャットルーム番号をパラメータとして渡すように!

C菜


A子
テーマの名前(この例では「テスト」)の上にマウスカーソルを当てると、左下にリンク先のURLが出るんだけど、ちゃんと「chat/index/1」って感じで部屋番号が指定されてるし…


B美
(てか、難しいのはJavaScriptの部分なんだけど…)
なので、私のほうで作ってみたわ







C菜

A子
下のほうにある「メッセージを入力」って箇所に何か入れて、「送信」ボタンをクリックすれば良いんだね?


B美
あ、必ず事前に「WebSocketサーバ」を起動しておきなさいね
(もしも起動前だったら、いったんチャットルーム一覧に戻って、再度入室すること)

C菜
それでは「MATE端末」を開いてっと
cd html/authapp[Enter]
bin/cake web_socket_server &[Enter] |

A子
(じゃないと、会話できてるかどうかがわからないからね)

↓


B美

↓


A子
(こうやってリアルタイムにやり取りできるのって、なかなか面白いね)
てか、仕事しとるがな


C菜
まさに「チャット」になってますよ~

A子

C菜
(下の画面の「左から右へ」)


A子
(下の画面では「中→右→左」の順番)


C菜
ほんと、すごいですよ~

B美
「src/Command」の中にある「WebSocketServerCommand.php」にフィールドの追加と「initialize」メソッドの書き換えを行います
(要するに、データベーステーブルを利用するための準備)
class WebSocketServerCommand extends Command implements MessageComponentInterface
{ protected array $clients = []; protected array $rooms = []; private $Comments; public function initialize(): void { parent::initialize(); $this->clients = []; $this->rooms = []; $this->Comments = $this->fetchTable('Comments'); } |
次に「onMessage」メソッドを以下のように書き換えてね
public function onMessage(ConnectionInterface $from, $msg): void
{ $data = json_decode($msg, true); if (!isset($data['type']) || !isset($data['room_id'])) { return; } $roomId = $data['room_id']; $userId = $data['user_id']; if ($data['type'] === 'join') { $from->roomId = $roomId; if (!isset($this->rooms[$roomId])) { $this->rooms[$roomId] = []; } $this->rooms[$roomId][$from->resourceId] = $from; } elseif ($data['type'] === 'message' && $from->roomId !== null) { $messageData = [ 'room_id' => $roomId, 'user_id' => $userId, 'user' => $data['user'], 'message' => $data['message'] ]; //データベースへの格納 $comment = $this->Comments->newEmptyEntity(); $comment->content = $data['message']; $comment->room_id = $roomId; $comment->user_id = $userId; $this->Comments->save($comment); foreach ($this->rooms[$roomId] as $client) { $client->send(json_encode($messageData)); } } } |
あ、「WebSocketサーバの再起動」とキャッシュファイルの「パーミッション変更」を忘れずに!



C菜
cd html/authapp/tmp/cache/models[Enter]
su[Enter] chmod 666 myapp_cake_model_default_chat_rooms[Enter] chmod 666 myapp_cake_model_default_users[Enter] chmod 666 myapp_cake_model_default_comments[Enter] exit[Enter] |
三つのテーブル(のキャッシュファイル)全てを「666」にしないと、警告が出ちゃいますよ~
(多分、「comments」の外部キーに「user_id」と「room_id」が存在するせいだと思うんですけど~)


B美
「Cache」要素の中の「default」と「_cake_translations_」と「_cake_model_」のそれぞれに、次の一文を追加するの
'mask' => 0666, |
もちろん、設定後は必ず「キャッシュクリア」してね
cd html/authapp[Enter]
bin/cake cache clear_all[Enter] |
これにより、今後(自動的に作られる)キャッシュファイルは、全て「666」のパーミッションになるのよ(めっちゃ楽)


↓


C菜

A子
まずは「src/Controller」の中にある「ChatController.php」の書き換えだね
class ChatController extends AppController
{ private $Comments; public function initialize(): void { parent::initialize(); $this->Comments = $this->fetchTable('Comments'); } public function index($room_id = null) { //引数がなければトップページへリダイレクト if (is_null($room_id)) { return $this->redirect(['controller' => 'Top', 'action' => 'index']); } $comments = $this->Comments->find() ->contain(['Users'])->where(['room_id' => $room_id])->orderBy(['Comments.id' => 'ASC']); $this->set(compact('comments')); $this->set(compact('room_id')); } } |


C菜
<div id="chat-box">
<!-- 過去ログ --> <?php foreach ($comments as $comment) { ?> <?php if ($comment->user_id == $userId) { ?> <!-- 自分の発言 --> <div class="message sent"> <p><strong><?= $comment->user->handle_name ?></strong></p> <p><?= $comment->content ?></p> </div> <?php } else { ?> <!-- 他人の発言 --> <div class="message received"> <p><strong><?= $comment->user->handle_name ?></strong></p> <p><?= $comment->content ?></p> </div> <?php } ?> <?php } ?> <!-- メッセージがここに追加される --> </div> |


B美
それじゃ、テストしてみましょうか
新しくチャットルームを作って、まずは二人で会話してみましょう
そうねぇ、私(B美)とA子で…
で、あとからC菜に入室してもらうんだけど、そのとき過去の発言がきちんと表示されるかどうかのテスト…ってことね

A子
んじゃ、私が新たなチャットルームを作るね


B美


C菜


C菜
んで、私も発言してみました~
(下記参照)
あ、一つ気になったことが~
入室直後なんですけど、過去の発言の先頭(一件目)が表示されているんですよね~
(最新の発言まで「自動スクロール」したほうが良いのではないでしょうか~?)


B美
最初の発言から読んでいきたい人もいるだろうし…

A子

B美
(具体的には、Scriptタグの中の「addMessage」関数の末尾)

C菜
過去ログを表示するコードの下に、そのJavaScriptの実行を入れてみるです~
<script>
const comments = document.getElementById("chat-box"); comments.scrollTop = comments.scrollHeight; </script> |
で、どうでしょうか~?


A子
最新の発言者名のところまでは(自動スクロールで)表示されるんだけど、その発言内容までは見えてないのよね(なぜか…)
まぁ、些細なことだけどね…(苦笑)

B美
(というか、最後に打ち込んだJavaScriptのコードについては、次回には不要になるのよね(苦笑))
それじゃ、今回はここまで!
次回はもう少しこまごまとした改良を行っていきましょう

C菜