LoginSignup
0
0

More than 3 years have passed since last update.

チャットアプリで、1グループのメッセージ数上限を50件にする(Laravel)

Last updated at Posted at 2020-05-30

はじめに

初心者による初心者向けの記事です。
陥りやすい様々な苦手分野も交えて書いてみました。
「collectionと配列の違い」「モデルインスタンスってなんやねん!」「クエリビルダとかメソッドとかわからん!」「ネットでcount()関数について調べてから使ったのに、エラーになる!」など。

このように実装しようと思った背景

実用的なチャットアプリを作りたいため、今回の実装をしました。
テーブル関係はgroups hasmany messagesの1対多です。
私はAjax通信のリアルタイムチャットアプリを作成しています。LINEのグループチャット的なものです。
トークルームのAjaxは、では5秒おき(setTimeout("get_messageC()",5000);)にそのグループの全メッセージを取得し、表示してあるメッセージを全部消して(.remove();)全部書き足す(.append(html);)という実装をしています。
しかしこれでは、メッセージが1000,2000と増えるごとに動作が重くなってしまいます。よって、1グループメッセージは50件までしか保存されないような設計にしました。そうすれば最大でも50件しか書き換えと表示が行われません。

やること

★メッセージ送信アクション時に、そのgroup_idと関連づいたmessageの数を数える(ここでcollectionと配列の違いを理解しとく必要がある)→
★50件以上あるなら、上位50件以外のメッセージは削除する(ここでモデルインスタンスなのかDBインスタンスなのか把握しておく必要がある)

下図messageテーブルです。ターミナルにてmysqlで出しました。見づらいですが、messagesテーブルのカラムにgroup_idがあります。user_idはグループ作成者です。いざという時の拡張性を考えて入れときました。
スクリーンショット 2020-05-30 11.50.52.png

collectionと配列の違い

collectionはLaravelが提供する高機能な配列です。一方で配列はみなさんがよく見かけるphpの「the・普通の配列」です。
データをインスタンス化した時はcollectionであることが多いです。例えば下記のようにモデルをインスタンス化した時。
例:$apple = new Apple;
下記のように自分で配列を作った時は、紛れもなくphpの配列です。
例:$apple = array('iphone','macbook' ,'ipad');

違いを理解すべき瞬間

厄介なのは、「配列の要素数を数えるcount関数」1つとっても、collectionと配列で文法が異なることです。先ほどの$appleを例に取りましょう。
collectionの場合$count = $apple->count();
配列の場合$count = count($apple);
ちなみに配列関数count()をcollectionに適用すると以下のようなエラーが出ます。
ErrorException (E_WARNING)
count(): Parameter must be an array or an object that implements Countable

「count()関数は引数は配列かオブジェクトじゃないとダメですよ〜」ということですね。collectionを配列とみなしていないようです。
ちなみに僕もなんのインスタンスがcollectionになるのか、というのは把握していません。エラー文見てその都度判断してます。

書いたコード

先にクラスをuseしちゃいましょう。自分のコントローラーにあるものをコピペします。

今回使用するクラス

注目すべき箇所は「★」をつけてます。今回は★をuseしておけば問題ないと思います。

GroupController.php
<?php

namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB; //★
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Auth;
use Illuminate\Support\Facades\Hash;
use App\User;
use App\Follow;
use Abraham\TwitterOAuth\TwitterOAuth;
use App\Group;
use App\Message; //★
use Illuminate\Support\Facades\Log;
use Storage; 
GroupController.php@send
///まず現在チャットしているgroupのメッセージ数を全て取得します。
    $count = $message->where('group_id',$_POST['group_id'])->count();
///その数が50を超えているなら、
    if ($count > 50) {
///現在チャットしてるグループがhasするmessageのidを取得するためにやはりまずメッセージを取得します
      $deleteid = DB::table('messages')
      ->where('group_id', $_POST['group_id'])
///それの並び順を古い順から新しい順に変えます。
      ->orderBy('id','desc')
///messageデータを50件のみ取得します(新しい順なので最新50件)。
      ->take(50)
///idのみ抽出します(必要ないけど、動作軽くなるかなーと思って)
      ->pluck('id')
//最新50件のなかで最も古いメッセージのidを取得。これで$deleteidに最も古いメッセージのidを入れました。
      ->min();

      Message::where('group_id',$_POST['group_id'])
//$deleteidより古いメッセージは全て削除。
      ->where('id','<',$deleteid)->delete();
    }

このコードより前で$message = new Message;しました。つまり$messageは、Messageモデルのインスタンスです。group_idはAjaxのtype: "POST",で送信しているので、$_POST['group_id']で受け取ります。type: "GET",の場合やそもそもtype指定してない場合は$_GET['group_id']で受け取ってください。type指定しないと自動でtype: "GET",になるので。

tips

なぜ$messageがあるにもかかわらずif(){}内でわざわざDB::table('messages');しているのか。なぜ$messageを使用しないのか。
それは、$messageはcollectionだからです。Messageモデルのインスタンスなのでcollectionになります。
クエリビルダのメソッドを使いたいです。->orderBy()->take()->pluck()を使いたいです。DBのインスタンスじゃないとクエリビルダメソッドは使えません。
ちなみにuse Illuminate\Support\Facades\DB; //★クラスをインスタンス化してます。

また、$messageにクエリメソッドをしようすると、Object of class Illuminate\Database\Eloquent\Builder could not be converted to stringエラーが出ます。
一番下でまたクラス呼んでいますが、なぜそうしたか自分でもわかりません。昨日の自分に問い質したいです。
Message::where('group_id',$_POST['group_id'])
->where('id','<',$deleteid)->delete();

最後に

よく「ドキュメント読め」とか「オブジェクト指向を理解しろ」と言われますが、それが全てだと僕は思いません。
ドキュメントが理解できるようになったのは学習開始5ヶ月目とかですし、平均1日6時間とか勉強した挙句のそれです。
僕が要領悪いだけかもしれませんが、是非僕のような初心者の方の役に立てたら幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0