Friction River Software

  • お問い合わせ

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

B美

今回は画面周りを整えていくよ

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

C菜

ちょっと見栄え良く作ってみるです~

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

A子

おぉ、メニューの上にマウスカーソルを乗っけると、色が変わるんだね



B美

なかなか良いわね
それじゃ、次は「templates/ChatRooms」の中にある「index.php」よ

「ChatController」の「index」メソッドへのリンクを作るんだけど、チャットルーム番号をパラメータとして渡すように!

C菜

こんな感じでどうでしょうか~?

A子

良いと思うわよ

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

B美

問題は「templates/Chat」の中に新規作成する「index.php」というファイルなんだけど、その中身がかなり難しいのよね
(てか、難しいのはJavaScriptの部分なんだけど…)

なので、私のほうで作ってみたわ






C菜

CSSJavaScriptがほとんどで、HTML部分はほんの少しなんですね~

A子

ブラウザで表示してみたら、こんな感じになったよ

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

B美

Enterキーでも送信できるようにしてるから、わざわざマウスに手を伸ばす必要は無いわよ

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

C菜

了解しました~
それでは「MATE端末」を開いてっと

cd html/authapp[Enter]
bin/cake web_socket_server &[Enter]

A子

あ、さっきはB美のアカウントで試したけど、別のPCから私(A子)のアカウントで(ログインして)チャットルームに入ってみるよ
(じゃないと、会話できてるかどうかがわからないからね)



B美

私のほうにそのメッセージが送られてきたから、適当に返信しとくわね



A子

お、ちゃんと届いたよ
(こうやってリアルタイムにやり取りできるのって、なかなか面白いね)

てか、仕事しとるがな

C菜

すごいです~

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

A子

ちょっと三人でチャットしてみようよ

C菜

最初は「B美部長」→「A子社長」→「私(C菜)」の順に発言してみましょう~
(下の画面の「左から右へ」)

A子

んじゃ、続けて「私(A子)」→「C菜」→「B美」の順ね
(下の画面では「中→右→左」の順番)

C菜

ほかの人の画面が書き換わるのが、即時リアルタイムなんですよね~

ほんと、すごいですよ~

B美

さて、あとは過去ログ(過去の発言)をデータベーステーブル(comments)に格納する処理を追加しないとね

「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美

パーミッションの変更が面倒だったら、「config/app.php」を変更する…って手もあるわよ
「Cache」要素の中の「default」と「_cake_translations_」と「_cake_model_」のそれぞれに、次の一文を追加するの

'mask' => 0666,

もちろん、設定後は必ず「キャッシュクリア」してね

cd html/authapp[Enter]
bin/cake cache clear_all[Enter]

これにより、今後(自動的に作られる)キャッシュファイルは、全て「666」のパーミッションになるのよ(めっちゃ楽)




C菜

こちらのほうが楽で良いかもです~

A子

それじゃ次は、過去ログをデータベーステーブル(comments)から読み込んで、チャット画面に表示してみよう

まずは「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菜

それでは対応する「templates/Chat」の中の「index.php」を書き換えますね~

<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美

「templates/Chat」の「index.php」の中に書いたJavaScriptの中に、すでにその記述があるじゃない
(具体的には、Scriptタグの中の「addMessage」関数の末尾

C菜

あ、わかりました~

過去ログを表示するコードの下に、そのJavaScriptの実行を入れてみるです~

<script>
    const comments = document.getElementById("chat-box");
    comments.scrollTop = comments.scrollHeight;
</script>

で、どうでしょうか~?

A子

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

まぁ、些細なことだけどね…(苦笑)

B美

余裕があれば、自分で色々と試行錯誤してみなさいね
(というか、最後に打ち込んだJavaScriptのコードについては、次回には不要になるのよね(苦笑))

それじゃ、今回はここまで!
次回はもう少しこまごまとした改良を行っていきましょう

C菜

了解です~