Friction River Software

  • お問い合わせ

CakePHP5入門【CakePHP5実用編④】チャットルーム

A子

まずはチャットルーム(テーブル名は「chat_rooms」)のCRUDを作ろうか

bake all」で作っちゃっても大丈夫だよね?

C菜

良いと思います~

あ、ただ発言内容(テーブル名は「comments」)のほうは、「bake model」だけにしたほうが良いんじゃないでしょうか~?

A子

あー、たしかにね
んじゃ、さっそくbakeコマンドを叩きますか

cd html/authapp[Enter]
bin/cake bake all chat_rooms[Enter]
bin/cake bake model comments[Enter]

で、どう?




B美

bakeしたあとに言うのもなんだけどさー

テーブルの項目名って、工夫することでコード修正の手間が軽減されるのよね
(言い換えれば、法則性を無視した名付けを行うとコード修正の手間が発生するってことなんだけど…)

A子

え?
どういうことよ

B美

自動生成されたファイルの中身を見てみれば一目瞭然よ

・「src/Model/Entity」の中に生成された「ChatRoom.php」及び「Comment.php」
・「src/Model/Table」の中に生成された「ChatRoomsTable.php」及び「CommentsTable.php」
・「src/Controller」の中に生成された「ChatRoomsController.php」

これらのファイルには、なぞの「admin」や「room」という項目名や「Admins」や「Rooms」というクラス名が(存在しないのに)指定されてるから…(苦笑)







C菜

あ、なんとなく分かりました~

「chat_rooms」テーブルの外部キー「admin_id」、「comments」テーブルの外部キー「room_id」が原因じゃないですか~?
(おそらく「comments」テーブルのもう一つの外部キーである「user_id」は問題ない気がします~)

B美

大正解!
さすがはC菜ね

外部キー項目の名前って、「参照先のテーブル名の単数形+'_id'」にするのがCakePHPの「命名規則」なの
(「admin_id」は「user_id」に、「room_id」は「chat_room_id」にすべき…ってこと)

まぁ、(規則に反していても)bake後に手動で修正していけば良いだけなんだけどね(苦笑)

A子

先に言えや!

まぁ良いわ
とりあえずはModel関係のファイルを修正していこう

Entityファイルからは存在しない項目名を削って、Tableファイルのほうは「Admins」を「Users」に、「Rooms」を「ChatRooms」に変更しただけなんだけどね
(「ChatRoomsController.php」については、あとで変更するよ)






C菜

二つのEntityファイルですけど、「$_accessible」の中身を勝手に削除しちゃっても良いのでしょうか~?
(「ChatRoom.php」と「Comment.php」のことです~)

B美

このケースだったら、まず大丈夫でしょう
(コントローラの処理内容によっては、問題が発生する場合もあるけど…)

まぁ、あまり気にすることは無いわ

C菜

安心しました~
それでは次に見栄みばえを整えましょう~

「templates/ChatRooms」の中にある「index.php」「add.php」「edit.php」「view.php」を書き換えますね~
(「templates/Users」の中にある同名のファイルを参考にして…)

まずは「index.php」からです~


A子

連動して「ChatRoomsController.php」の「index」メソッドのほうも書き換えるよ
(注意点としては、「Users」をcontainすることかな)

あ、検索条件としては、削除フラグ(delete_flag)が「0」であるもの…って指定したからね
(削除されたレコードの「delete_flag」には「1」が入る…ってこと)

C菜

次は「add.php」です~

ポイントは下のほうにあるhidden(隠し)属性の二行ですよ~

A子

ChatRoomsController.php」の「add」メソッドが以下の記述なんだけど、上でC菜が言った「hidden属性の行」があるおかげで、ほとんど修正しなくても大丈夫だったよ

C菜

それでは「edit.php」です~

A子

対応する「edit」メソッドがこれね

こっちもほとんど修正してないわ

C菜

最後は「view.php」です~

A子

view」メソッドも(「index」と同様)containするのは「Users」だからね

B美

削除の「delete」メソッドについては、前回(画像投稿掲示板)のコードを流用すれば良いわ

A子

OK
ChatRoomsController.php」の「delete」メソッドがこれね

B美

補足だけど…
「contain」に「Users」を指定しているのは、「chat_rooms」テーブルと「users」テーブルを「結合(Join)」するためよ
(【CakePHP5基礎編⑦】を参照)

まぁ、ユーザ情報については全く使っていないみたいだけど…(苦笑)

C菜

それじゃ、「view.php」に管理人のハンドルネームを表示してみるです~

えっと~
$chatRoom->handle_name」で良いんでしょうか~?

B美

やってみれば分かるわよ

まずは「チャットルーム作成」を行ってみなさい
そのあと「表示」をクリックしてね



C菜

何も出ませんね~(苦笑)

B美

それじゃ、右下の「DebugKit」アイコンをクリックしてから、右端にある「Variables」をクリックしてね

chatRoom」という項目の右端にある右三角をクリックすると詳細な内容が表示されるから、その中の「user」という項目の右三角をクリック
その中にある「handle_name」という項目の値を見てちょうだい



A子

お、「B美」って出てるじゃん

んー
てことは、「$chatRoom->user->handle_name」ってことかな?

C菜

多分、そうですよ~

view.php」を書き換えてから、ブラウザで確認してみるです~



A子

うん、ばっちりだね
それじゃ、同じように「index.php」のほうにも管理人のハンドルネームを表示してみようか

あれ?
results」の中の「items」までは出るけど、その中身が出ないよ

B美

DebugKitの階層の深さって、デフォルトでは5階層までと設定されてるのよ
これを変えたければ、「config」ディレクトリの中の「bootstrap.php」か「app.php」または「app_local.php」に設定を追記すれば良いわ

そうねぇ
簡単なのは「app_local.php」の末尾に以下の一文を追加することかしら
(これにより、デフォルトの5階層から8階層へと変更されるわ)

'DebugKit.variablesPanelMaxDepth' => 8,

あ、「bootstrap.php」のほうで設定する場合は以下の一文だから間違えないようにね

Configure::write('DebugKit.variablesPanelMaxDepth', 8);

あと、これは両方を設定するんじゃなくて、どちらか一方だけ設定すればOKだから
(私は「app_local.php」のほうに書くけどね)

C菜

今度は深い階層まで表示されましたよ~

A子

なるほど、「view.php」のときと同じだね
(「user」から、たどれるってこと)

…ってことは、「index.php」をこう書き換えよう

B美

惜しい!
一点だけ問題があるわ

<?= $this->Paginator->sort('handle_name', '管理人') ?>

の箇所だけど、

<?= $this->Paginator->sort('Users.handle_name', '管理人') ?>

に変更してね



A子

その「Users.handle_name」って、containした表が「Users」だからなの?

B美

その通り!
これによって「管理人」という項目名のクリックで、ハンドルネームにもとづくソートが可能になるのよ

「なんて簡単な!」って思ったでしょ?

A子

まぁね

ただ、知らないとハマりそうだけど…(苦笑)

C菜

ですね~

あ、あと管理人以外は「編集」や「削除」ができないようにしましょう

B美

ふむ

その場合、リンクの表示・非表示だけじゃなく、「edit」や「delete」のメソッド側のほうも変更しておきなさいね
(リンクが表示されてなくても、直接URLで指定される可能性もあるんだから…)

C菜

了解です~

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


A子

ねぇ、「ChatRoomsController」の親クラスである「AppController」のほうでも「getIdentity」メソッドを実行してるよね?

その結果って「ChatRoomsController」クラスのほうで利用できないの?
(下記参照)

C菜

継承」って、親の能力を子が受け継ぐんですよね~

だったら、親クラス側にフィールドを定義しておいて、そこに「getIdentity」メソッドの戻り値を代入しておけば良いんじゃないでしょうか~?

class AppController extends Controller
{
    public function beforeFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);

        $identity = $this->Authentication->getIdentity();
        $this->set(compact('identity'));
    }

の箇所を

class AppController extends Controller
{
    private $identity;

    public function beforeFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);

        $this->identity = $this->Authentication->getIdentity();
        $this->set('identity', $this->identity);
    }

という風にするんですよ~

A子

あっ、そうか!
だったら、さっきの「edit」メソッドと「delete」メソッドの中にあった

$identity = $this->Authentication->getIdentity();
if ($chatRoom->admin_id != $identity->id) {
    return $this->redirect(['action' => 'index']);
}

の箇所を

if ($chatRoom->admin_id != $this->identity->id) {
    return $this->redirect(['action' => 'index']);
}

に変更できるね
(よりシンプルになる)

C菜

やってみますね~

…って、めっちゃエラーです~(泣)

B美

考え方は正しいんだけど、問題は「アクセス修飾子」の知識だけね

「AppController」クラスに追加したフィールド「$identity」に付けている「private」って、自分自身(「AppController」クラス)からしかアクセスできないことを意味するのよ

A子

え?そうなの?

んじゃ、子クラスにもそのフィールドを引き継ぐにはどうすりゃ良いのさ?

B美

簡単よ
「private」ではなく「protected」にするだけ

class AppController extends Controller
{
    private $identity;



class AppController extends Controller
{
    protected $identity;

に変更すればOKってこと

ちなみに、もう一つ「public」という修飾子もあるけど、これはフィールドに付けちゃダメなやつだからね
(「フィールド隠蔽いんぺい」の考え方に反するので…【CakePHP5基礎編②】を参照)

あと、勘違いしてるみたいだから指摘しておくけど…

親クラスのprivateフィールドであっても、子クラスには引き継がれるわよ
ただ、「子クラスからはアクセスできない」フィールドになる…ってだけね

A子

な、なるほど…
んじゃ、そういう風に書き換えてっと

おぉ!
ばっちり動いたよ



C菜

これで(「ChatRoomsController」だけじゃなく)どんなコントローラークラスからでも、ログインユーザの情報にアクセスできるようになった…ってことですね~
(「$this->identity」という形で…)

完璧です~