【PHP/Laravel】多対多のテーブルで相互フォローのユーザを取得する方法。
こんにちは、シロウです。
以前まではテックアカデミーのUnityコースを受講しており、独学期間も合わせると5ヶ月ぐらいはUnityの学習をしていたのですが、、、何かUnity(ゲーム開発エンジン。要はフレームワーク的な)ってあまりにも便利すぎてコードを書くことが少ないんですよね、、、笑笑
どっちかというと、僕はコードを書くことが好きなので、この度思い切ってWeb系の学習に切り替えてみました。
目標はフロントエンドもサーバーサイドもどっちも極めるべき毎日8~9時間ほどパソコンと向き合いつつ、筋トレと学校の課題をする日々を送っています。
そんな学習中の僕ですが、只今プログラミング学習者向けのマッチングアプリを作成しており、とりあえず一通りの機能は完成しました。
そんな中で「相互フォローを確認する」という処理を実装するのに少し手間取ってしまったので、メモ&少しでも誰かの役に立つために記事にしておきます。
さて、それでは早速ですがみていきましょう。
【下準備】フォローとフォロワーの多対多の関係を作る
さて、とりあえず「相互フォローかを確認する」の前に「フォローとフォロワーの関係」を作らないといけません。
「自分がフォローする人」はたくさんいますし、「自分をフォローしている人」も沢山いるので、この場合は「フォローとフォロワー」の関係は「多対多の関係」となります。
なので、まずはフォローとフォロワーの関係を表す中間テーブルを作って、Userモデルにもその関係を示してあげないといけません。
ということで、まずは下記コードを記述したマイグレーションファイルを作成&実行して、中間テーブルを作ります。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserFollowTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_follow', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('follow_id');
$table->timestamps();
// 外部キー制約
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('follow_id')->references('id')->on('users')->onDelete('cascade');
// user_idとfollow_idの組み合わせの重複を許さない
$table->unique(['user_id', 'follow_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_follow');
}
}
これで中間テーブルは完成です。
中間テーブルでは外部キー制約は必須。また、同じ人同士のフォローフォロワーの関係は重複しないので、uniqueを使って重複しないという設定を加えておきます。
こうすることで、例えばuser_id「1」の人がfollow_id「2」の人をフォローすると、これ以上『user_id「1」、follow_id「2」というレコード』は作られません。*user_id「2」、follow_id「1」のレコードは作成されます。当たり前ですが、、、
あと中間テーブルを作ったら、その関係を示す記述もModelにしてあげなければなりません。
(正確には別に記述しなくてもいいですが、記述しないとそもそも関係性を取得出来ないので、中間テーブルを作った意味がほとんどないって感じです。)
なので、下記をUserモデルに記述してあげます。
//このユーザがフォローしている人を取得
public function followings()
{
return $this->belongsToMany(User::class, 'user_follow', 'user_id', 'follow_id')->withTimestamps();
}
//このユーザをフォローしている人を取得
public function followers()
{
return $this->belongsToMany(User::class, 'user_follow', 'follow_id', 'user_id')->withTimestamps();
}
多対多の時は「belongsToMany」を使います。
あとwithTimestamps()これをつけておけばcreated_atとかも追加されるので便利だよって感じで深く考える必要はないです。
こうすることで、例えば「$Auth::user()->followers」とかすれば$thisにログインしているユーザになるので、ログイン中のユーザのフォロワーを取得という処理ができるようになります。
相互フォロー中のユーザを取得
さて、ではいよいよ本題です。
まぁ、下記のコードを先ほどと同じくUserモデルに記述してあげればOKです。
public function follow_each(){
//ユーザがフォロー中のユーザを取得
$userIds = $this->followings()->pluck('users.id')->toArray();
//相互フォロー中のユーザを取得
$follow_each = $this->followers()->whereIn('users.id', $userIds)->pluck('users.id')->toArray();
//相互フォロー中のユーザを返す
return $follow_each;
}
あとはこれで、view系のファイルの中で「Auth::user()->follow_each()」とかで呼んでforeach文で回してあげれば順番に相互フォロー中のユーザを取得できます。
コードの解説
$userIds = $this->followings()->pluck('users.id')->toArray();
まずはこれから解説します。
最初の$thisは同じクラス内のfollowings()
を呼び出すために必要(phpの構文)なので、とりあえず記述しておいて、そこからfollowings()
を呼び出して、このユーザ、つまりこの関数をviewファイル内で呼び出したユーザがフォローしている人(follow_idを取得して、それと紐づいているusersテーブルのレコード情報
)を取得します。
$thisには関数を呼び出したインスタンスが代入されますから、このような挙動になります。
つまり、view系のファイル内で「Auth::user()→followings()」とすればログイン中のユーザがフォローしている人を取得するし、$userとかで他のユーザの情報を渡しているなら「$user->followings()」とすれば$userに格納されているユーザがフォローしている人たちを取得できます。
そして、pluck('users.id')
は指定したカラムのデータだけを取得するので、今回の場合はusersテーブルのidを取得。という意味になります。
でもって、最後のtoArray()
で配列にしています。(ここは僕もイマイチ理解できていませんが、とりあえず配列に直さないと上手く使い回せないので、とりあえず配列にします。)
$follow_each = $this->followers()->whereIn('users.id', $userIds)->pluck('users.id')->toArray();
でもって、次はこいつです。
まずは呼び出したユーザのフォロワーをfollowers()
で取得して、次にwhereInメソッドを使います。
これは第一引数に指定したカラムの中に第二引数に指定した配列の値を取得するメソッド。
つまり、今回の場合はまずfollowers()
でfollow_id
と紐づいているusersテーブルのレコードを取得して、その中からさらに(usersテーブルの)idが$userIds
(ユーザがフォローしている人)と同じidを持つ人を取得することで相互フォローのユーザを取得しています。
あとはid以外の情報がいらないので、先ほど同様idだけ取得して、最後に配列に直します。
あとはviewファイル内で下記のように記述すればOKです。
@foreach ($users as $user)
@if(in_array($user->id,Auth::user()->follow_each()))
~~~~~~処理内容~~~~~~~~~
@endif
@endforeach
in_array()は第一引数に指定した値が配列にあるかどうかを確認する値なので、ユーザを1人ずつチェックして、もし相互フォローだったら処理を実行って感じです。
ふぅ、長くなりましたが、これにて完了です。執筆時間は1時間ちょうどぐらい。よし、引き続きLaravel触ります。
何か質問とかあればshiro_life0までお願いします。1日以内に返します。
それでは、また(*・ω・)ノ