Friction River Software

  • お問い合わせ

CakePHP5入門【CakePHP5実用編⑬】ブラッシュアップ

B美

さて、だいたいの開発は完了かな?

そろそろインターネット上に公開する?

A子

そだね

いや、もう少し機能強化を進めていこう

C菜

どうするんですか~?

A子

チャットルームについては、「text」型の説明文を追加したいんだよね

あと、テーブルのフェッチをスーパークラスである「AppController」クラスにまとめちゃおう
(今は各クラスの「initialize」メソッドで個別にフェッチしてるけど…)

B美

リファクタリングってわけね

A子

り…何だって?

B美

「リファクタリング」…

プログラムの動作を変えずに、ソースコードのムダを省いたり、効率化すること

C菜

たしかにテーブルフェッチを親クラスで行ったほうが良いかもです~

あと、Webアプリケーションの名称を正式決定しましょう~
バージョンを「1.0.0」にしたり、「DebugKit」アイコンを消したりも…)

B美

システム名称だけど「Beビー Chatteringチャタリング」なんてどうかな?
(「おしゃべりしよう」って意味で…)

あ、それから前回(チャットルームの入室制限)は口出ししなかったけど、例外リストに表示するユーザの並び(表示順)はあれで良いの?

C菜

そういえば、並べ替えをやってなかったですね~

ユーザID(メールアドレス)」か「ハンドルネーム」の昇順にしたほうが良いかもです~

A子

日本語の並べ替えがうまく働くか分かんないから、「ユーザID(メールアドレス)」の昇順にしようか

よし、それじゃ、まとめるよ

1.「chat_rooms」テーブルに「text」型の項目(description)を追加する
2.テーブルのフェッチを「AppController」クラスで行う(フィールドは「protected」にする)
3.「config/const.php」でシステム名称を「Be Chattering」に、バージョンを「1.0.0」にする
4.「DebugKit」アイコンを非表示にする
5.例外リストに表示するユーザの一覧を「ユーザID(メールアドレス)」の昇順でソートする

…くらいかな?

C菜

良いと思います~

あ、各ユーザの人物アイコンって、チャット画面上に表示できないでしょうか~?
(LINEみたいに…)

B美

画像を「ファイルとして保存」する方法って、前にやったの憶えてるかしら?
(掲示板アプリのときの話)

C菜

大丈夫です~

あれを応用すれば良いんですね~

A子

画像の保存方法って、ほかにもあるの?

B美

データベース内にBLOBブロブ型のデータとして記録する方法もあるわよ

まぁ、画像ファイルのようにブラウザキャッシュもかないし、画像データ読み出しの処理が(システムにとっての)負担になるからおすすめしないけど…

A子

なるほどねぇ

あ、「users」テーブルの主キーである「id」の値を使ってファイル名を付けるのはどうかな?
(「1.png」とか「2.png」のように)

C菜

あっ!
それなら「users」テーブル内にファイル名(及びファイルパス)を保存するための項目を追加しなくて済みますね~

A子

そういうこと

人物アイコンの画像ファイルを格納するディレクトリは固定しておいて、かつファイル形式(拡張子)もPNGに決め打ちしておけば、さっきの「idをファイル名にする方法」でいけるじゃん

B美

ふむ
だったら、ユーザの新規作成時にデフォルトの画像を割り当てるって方法が良いかもね

そうすればファイルの存在確認(file_exists関数)も不要になるし…

C菜

良いと思います~

それって、Windowsのユーザアイコンと同じ考え方ですよね~


…って、やつです~

A子

うん、良いね
まとめると

1.「chat_rooms」テーブルに「text」型の項目(description)を追加する
2.テーブルのフェッチを「AppController」クラスで行う(フィールドは「protected」にする)
3.「config/const.php」でシステム名称を「Be Chattering」に、バージョンを「1.0.0」にする
4.「DebugKit」アイコンを非表示にする
5.例外リストに表示するユーザの一覧を「ユーザID(メールアドレス)」の昇順でソートする
6.各ユーザの人物アイコンを実装する

…ってことだね

6番以外はすぐにできるから、ちゃっちゃとやっちゃおう

C菜

では、最初は1番ですね~

1.「chat_rooms」テーブルに「text」型の項目(description)を追加する

chat_rooms」テーブルのテーブル定義はこうしましょう~

create table chat_rooms (
    id int auto_increment primary key,
    theme varchar(255) not null,
    description text,
    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;

んで、「ALTER TABLE」はこうです~

mysql -u root -p chatdb[Enter]

ALTER TABLE chat_rooms
    ADD description text AFTER theme;[Enter]

A子

データベース構造を変更したから、念のためキャッシュクリアもやっておこう

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

C菜

「src/Model/Entity」の中にある「ChatRoom.php」の$_accessibleに以下の一文を追加しておきますね~
(忘れないうちに…)

'description' => true,

A子

よし、あとは掲示板アプリのソースコードを流用して、「templates/ChatRooms」の中にある「add.php」を修正しよう

C菜

やってみました~

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



A子

うん、良いね

edit.php」のほうも全く同じで良いかな?

C菜

良いと思います~

あとは「index.php」と「view.php」ですけど、これで良いでしょうか~?
(「index.php」のほうは、narrow表示のときだけ説明文を表示しています~)


B美

うん、問題は無さそうね(多分だけど…)
んじゃ、次は2番よ

2.テーブルのフェッチを「AppController」クラスで行う(フィールドは「protected」にする)

A子

調べてみたら、4つのControllerコントローラーでフェッチをやってたよ

具体的には「ApplicantsController.php」「ChatController.php」「ChatRoomsController.php」「TopController.php」の4つだね
これを「AppController.php」に集約するよ

protected $Users;
protected $Comments;
protected $ChatRooms;
protected $Exceptions;

public function initialize(): void
{
    ・・・

    $this->Users = $this->fetchTable('Users');
    $this->Comments = $this->fetchTable('Comments');
    $this->ChatRooms = $this->fetchTable('ChatRooms');
    $this->Exceptions = $this->fetchTable('Exceptions');
}

あ、もちろん「ApplicantsController.php」「ChatController.php」「ChatRoomsController.php」「TopController.php」のほうは、privateフィールドと「initialize」メソッドを削除したからね


C菜

次は3番です~

3.「config/const.php」でシステム名称を「Be Chattering」に、バージョンを「1.0.0」にする

これはすぐに済みますね~
あ、「HEAD_MAIN」だけじゃなく「APPLICATION_NAME」も同じ名前にしていますよ~


A子

んじゃ、4番だね

4.「DebugKit」アイコンを非表示にする

これもすぐに終わるよ
「config」の中にある「app_local.php」を書き換えるだけ…

C菜

それでは5番です~

5.例外リストに表示するユーザの一覧を「ユーザID(メールアドレス)」の昇順でソートする

「src/Controller」の中にある「ChatRoomsController.php」の「add」メソッドと「edit」メソッドの記述を書き換えますね~

$users = $this->Users->find()->all();
        ↓
$users = $this->Users->find()->order(['email' => 'ASC'])->all();


B美

OKよ
さて、最後は6番ね

6.各ユーザの人物アイコンを実装する

これが一番の問題なわけだけど…

A子

まずは画像のサイズ(縦横のドット数)を決めよう

LINEの場合はどうだったっけ?

C菜

ググってみました~

最低でも480ドット×480ドットらしいですよ~

B美

もっと小さくても良いのではないかしら

例えば100ドット×100ドットくらいで…

A子

だねー

単なるアイコン画像なんだから、もっと小さくても良いくらいだね

C菜

あまり小さすぎてもアレなんで、B美部長の案でいきましょうよ~

というわけで、Windowsの「ペイント」を使ってデフォルト画像を作ってみたです~

A子

うん、良いんじゃない?

てか、Windowsアイコンの丸パクリだけど(笑)

C菜

でも、何もないところから自作しましたよ~

創作性の有無が微妙なので、著作権は発生しないかもしれませんけど~(苦笑)

A子

それじゃ、画像ファイルの置き場所を作ろうか

cd html/authapp/webroot[Enter]
mkdir personal[Enter]

で良いかな?

C菜

次に「WinSCP」を使ってWindowsパソコンからSCP接続するです~
で、さっき「ペイント」で作った画像ファイルをアップロードしますね~

同じファイルをアップロードするつど、ファイル名を変更していくことで、5つのファイルを作りましたよ~
(現時点で4人のユーザがいるので、それぞれの分と、あとデフォルト画像として…)

A子

今後ユーザが追加されるたびに、「default.png」を元に「(新規ユーザのid).png」が作られるってわけだね

うーん、その処理も書かないと…

B美

「src/Controller」の中にある「UsersController.php」及び「ApplicantsController.php」を修正しないといけないわね

具体的には、「UsersController.php」のほうは「firstUser」メソッドと「add」メソッド、「ApplicantsController.php」のほうは「finalCheck」メソッドね

A子

あ、だったら親クラスである「AppController.php」の中に、privateメソッドとして(画像ファイルをコピーする処理を)記述すれば良いんじゃない?
(子クラスである「UsersController.php」や「ApplicantsController.php」から呼び出せるように…)

C菜

「private」ではダメだと思うです~

protected」じゃないと~

B美

C菜正解!

それじゃ、やってみなさい

A子

ChatGPTから教えてもらったコードをアレンジして、こんな感じで作ってみたよ
(もちろん、「AppController.php」の中ね)

protected function copyImage($id = null): void
{
    if (!is_null($id)) {
        $webroot = WWW_ROOT;
        $src = $webroot.'personal'.DS.'default.png';
        $dst = $webroot.'personal'.DS.$id.'.png';
        copy($src, $dst);
    }
}

で、このメソッドを「UsersController.php」や「ApplicantsController.php」から、こう呼び出すよ

$this->copyImage($user->id);

B美

ふむ、エラーチェックをやってないのは、まぁ良いとしても…

コード自体に問題は無いわ(多分…)
でも、これって必ず失敗するわよ

A子

え?
なんでよ

C菜

コードに問題ないのに失敗する…

あっ、パーミッションです~

B美

そういうこと

さっき作ったディレクトリって、Webサーバ(www-data)の書き込み権限が無いのよね
(パーミッションを変更するか、ディレクトリのオーナーを変更しないと…)

さらに言えば、さっきコピーしたテストデータのパーミッションも問題よ
(Webサーバが上書き処理できないから…)

C菜

ディレクトリとファイルのオーナーをWebサーバにしてみましょう~

cd html/authapp/webroot[Enter]
su[Enter]
chown www-data:www-data personal[Enter]
cd personal[Enter]
chown www-data:www-data *[Enter]
exit[Enter]

で、どうでしょうか~

A子

おぉ、さすがはC菜だね

んじゃ、あとは「UsersController.php」の「firstUser」メソッドと「add」メソッド、「ApplicantsController.php」の「finalCheck」メソッドに追記するよ



B美

うん、良いんじゃないかしら

次は、チャット画面の中に人物アイコンを表示する処理を書いてみなさいね

C菜

了解です~
「templates/Chat」の中にある「index.php」を書き換えますね~

・・・



C菜

・・・

ふぅ~、めちゃくちゃ大変でした~
まずは過去の履歴を表示する箇所ですけど~

<!-- 他人の発言 -->
<div class="fixed-size-image">
    <figure class="image"><?= $this->Html->image("/personal/{$comment->user->id}.png", [
        'width' => '100',
        'height' => '100',
        'class' => 'is-rounded'
    ]) ?></figure>
</div>

<div class="message received">
    <p><strong><?= $comment->user->handle_name ?></strong></p>
    <p><?= $comment->content ?></p>
</div>

これに絡めてCSSのクラス定義を追加します~

.fixed-size-image {
    width: 100px;
    height: auto;
    max-width: 100px;
    max-height: 100px;
    object-fit: contain;
}

んで、JavaScriptの関数部分がこれです~

function addMessage(senderId, user, message) {
    const chatBox = document.getElementById("chat-box");
    const msgDiv = document.createElement("div");

    msgDiv.classList.add("message");
    if (senderId == userId) {
        msgDiv.classList.add("sent"); //自分のメッセージ
    } else {
        msgDiv.classList.add("received"); //他人のメッセージ

        const image = document.createElement('img');
        image.src = `/authapp/personal/${senderId}.png`;
        image.width = 100;
        image.height = 100;
        image.classList.add("is-rounded");

        const imgDiv = document.createElement("div");
        imgDiv.classList.add("fixed-size-image");
        const figure = document.createElement("figure");
        figure.classList.add("image");
        figure.appendChild(image);
        imgDiv.appendChild(figure);

        chatBox.appendChild(imgDiv);

    }

    msgDiv.innerHTML = `<p><strong>${user}</strong></p><p>${message}</p>`;
    chatBox.appendChild(msgDiv);

    chatBox.scrollTop = chatBox.scrollHeight;
}













A子

うわっ、複雑さ(大変さ)が半端ねぇ…

さすがはC菜と言うべきか(苦笑)

B美

それじゃ、テストしてみましょうか

私とA子でチャットしてみるわね
まずは私から発言してみましょう
(左側が私で、右側がA子よ)

A子

んじゃ、次は私ね
(以下、交互に発言…)





C菜

どうやら大丈夫みたいですね~

個別の(異なる)アイコン画像にしないと、成功か失敗かが今一つわかりませんけど~(苦笑)

B美

うん、まぁ大丈夫でしょう
んで、ちょっと長くなっちゃったから、今回はここまでね
(キリの良いところで…)

次回は、人物アイコン画像の変更処理をやっていきましょう

A子

OK

たしかにちょっと疲れたよ…
(充実感はあるけどね(苦笑))