テーマ
どうも!!naoto555です。
今回は、Laravelを用いて昨今のSNSでは当たり前になったフォロー機能フォロワー機能を実装する方法をご紹介したいと思います。Usersテーブルの詳細や登録しているUsersデータのダミーデータの作り方に関しましては、「Seederで日本語のダミーユーザーを作成する(こちらの記事)」をご参考にして頂けると幸いです。
(2024.2記載)
開発環境
統合開発環境 | aws cloud9 |
PHP | 8.1.16 |
Laravel | 9.52 |
login機能 | Laravel/jetstream(livewire) |
Database | SQlite(version 3.7.17) |
解説
①Followモデルとテーブルの作成
まずは、誰が誰をフォローしているかを示すテーブルをモデルで作成していきます。
php artisan make:model Follow --all
database/migrations/2023_XX_XX_XXXXXX_create_follows_table.php
//一部抜粋
public function up()
{
Schema::table('follows', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id')->unsigned(); //自分のID
$table->bigInteger('follow_id')->unsigned(); //友達の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']);
});
}
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
の意味としては、followsテーブルのuser_idカラムは、Usersテーブルを親テーブルとしていてUserテーブルのIDを参照してテーブルにレコードしなさい。という意味です。
また、”onDelete(‘cascade’)”を書くことで、親テーブル(Usersテーブル)のレコードが削除された場合に、それに関連する子テーブル(followsテーブル)のレコードも自動的に削除する設定になります。
また、$table->unique(['user_id', follow_id]);
を書くことで重複した”user_id”と”follow_id”の組み合わせが登録されることを防ぐことができます。
このテーブルは下図のように、例えばuser(id=1)がuser(id=2)をフォローしたときに、それぞれのIDがfollowsテーブルに書き込まれるように設計していきます。
最後に、マイグレーションを実行し、追加したカラムをデータベースに反映させます。
php artisan migrate
②Usersモデルに関数メソッドを記載
続いて、Usersモデルに関数メソッドを書いていきます。
app/Modeles/User.php
use Illuminate\Support\Facades\Auth; //追加
//---------------------------- 中略 ----------------------------
//user_idのユーザーが、フォローしているユーザー(ID:follow_id)を抽出
public function follows()
{
return $this->belongsToMany(User::class, 'follows', 'user_id', 'follow_id');
}
//ログインユーザー(Auth::user())との関係性を返す
//0:どちらもフォローしていない 1:ログインユーザーが相手をフォロー 2:相手がログインユーザーをフォロー 3:お互いにフォロー
public function relation()
{
$id = $this->id;
//ログインユーザーが対象ユーザーをフォローしているか?をtrue/falseで返す
$follow = (boolean) Auth::user()->follows()->where('follow_id', $id)->first();
//対象Userが自分をフォローしているか?をtrue/falseで返す
$follower = (boolean) $this->follows()->where('follow_id', Auth::user()->id)->first();
if(!($follow) && !($follower)){ //0:どちらもフォローしていない
$result = 0;
}elseif($follow && !($follower)){ //1:ログインユーザーが相手をフォロー
$result = 1;
}elseif(!($follow) && $follower){ //2:相手がログインユーザーをフォロー
$result = 2;
}else{ //3:お互いにフォロー
$result = 3;
}
return $result;
}
//ログインユーザーは、対象ユーザーをフォローしているか?
public function isFollow()
{
$id = $this->id;
$isFollow = (boolean) Auth::user()->follows()->where('follow_id',$id)->first();
return $isFollow;
}
③コントローラの編集
続いて、コントローラの編集して、index機能,follow機能の追加を行います。Userコントローラ未作成の場合は、まず、以下のコマンドでUserコントローラを作成してください。
php artisan make:controller UserController --model=User
app/Http/Controllers/UserController.php
use Illuminate\Support\Facades\Auth; //追加
use App\Models\Follow; //追加
//--------------------- 中略 ---------------------
public function index()
{
$users = User::where('id','!=',Auth::user()->id)->get();
return view('users.index', compact('users'));
}
public function follow(Request $request)
{
$follow_id = $request->follow_id;
//ログインユーザーが対象のユーザーをフォローしているか?
$isFollow = (boolean) Follow::where('user_id', Auth::user()->id)->where('follow_id', $follow_id)->first();
if($isFollow){
$unfollow = Follow::where('user_id', Auth::user()->id)->where('follow_id', $follow_id);
$unfollow->delete();
}else{
$follow = new follow();
$follow->user_id = Auth::user()->id;
$follow->follow_id = $follow_id;
$follow->save();
}
return back();
}
④ルーティングの編集
routes/web.php
use App\Http\Controllers\UserController; //追加
//-------------------------- 中略 --------------------------
Route::get('/users/index',[UserController::class,'index'])->name('users.index');
Route::post('/users/follow',[UserController::class,'follow'])->name('users.follow');
⑤ビューの編集
続いて、ビューの編集していきます。ビューファイル未作成の場合は、まず、以下のコマンドでビューを作成してください。
#フォルダとファイルの作成
mkdir resources/views/users
touch resources/views/users/index.blade.php
resources/views/users/index.blade.php
<div style="margin:30px">
<table border="1">
<tr>
<th style="padding:10px;">ID</th>
<th style="padding:10px;">名前(name)</th>
<th style="padding:10px;">メールアドレス(email)</th>
<th style="padding:10px;">関係性</th>
<th style="padding:10px;">フォローボタン</th>
</tr>
@foreach($users as $user)
<tr style="padding:10px;">
<td style="padding:10px;">{{ $user->id }}</td>
<td style="padding:10px;">{{ $user->name }}</td>
<td style="padding:10px;">{{ $user->email }}</td>
<td style="padding:10px;">
@switch($user->relation())
@case(0) <span>N/A</span> @break
@case(1) <span>あなたがフォロー</span> @break
@case(2) <span>あなたをフォロー</span> @break
@case(3) <span>相互フォロー</span> @break
@default <span>N/A</span>
@endswitch
</td>
<td style="padding:10px; justify-content:center;">
<form method="POST" action="{{ route('users.follow') }}">
@csrf
<input name="follow_id" type="hidden" value="{{ $user->id }}" />
@if($user->isFollow())
<button type="submit" style="background-color: #efc9d2;">
フォロー解除
</button>
@else
<button type="submit" style="background-color: #ccffcc;">
フォローする
</button>
@endif
</form>
</td>
</tr>
@endforeach
</table>
</div>
⑥Seederを実行してユーザーデータを挿入してみる
以下のコマンドでユーザーデータをリセットし、以下のコマンドを打ちダミーユーザー10件を挿入する。
ユーザーのダミーデータの挿入する方法は、こちらの記事にまとめてあるのでご参照下さい。
#tinker起動
php artisan tinker
#Usersテーブルのデータをクリア
> User::truncate();
#tinker終了
> quit
#ダミーデータの挿入
php artisan db:seed --class=UserSeeder
⑦ログイン用のテストユーザーを登録
以下のコマンドを順に打ち、ログイン用のテストユーザーを登録
#tinkerの起動
php artisan tinker
#起動後、以コマンドを打っていく
> $user=new User();
> $user->name = "test_user";
> $user->email = "test@mail.example";
> $user->password = Hash::make('password');
> $user->save();
#tinkerの終了
> quit
これで、以下の内容でUsersテーブルに情報が追加されました。
name | test_user |
test@mail.example | |
password | password |
users/indexのビューをみてみると下記のような表示となります。
表示はされていませんが、ID=11にログイン用のテストユーザーが登録されています。
⑧FollowテーブルにSeederデータを挿入
テストユーザー(ID=11)と各ユーザーが、以下の関係性になるようにSeederを作成します。
ID=1~3はテストユーザーと相互フォロー
ID=4~6はテストユーザーのみフォロー
ID=7~9は相手のみテストユーザーをフォロー
ID=10はお互いにフォローなし
FollowSeeder.phpを以下のように編集します。
database/seeders/FollowSeeder.php
use App\Models\User; //追加
use App\Models\Follow; //追加
class FollowSeeder extends Seeder
{
public function run()
{
$data = [
//ID1~3は、相互フォロー
[1, 11],[2, 11],[3, 11],[11, 1],[11, 2],[11, 3],
//ID4~6は、ログインユーザーがフォロー
[11, 4],[11, 5],[11, 6],
//ID7~9は、ログインユーザーをフォロー
[7, 11],[8, 11],[9, 11]
];
foreach($data as $record){
Follow::create([
'user_id' => $record[0],
'follow_id' => $record[1],
]);
}
}
}
以下のコマンドで、シーダーを挿入します。
php artisan db:seed --class=FollowSeeder
完成図
作成したテストユーザーでログインしてビューをみてみます。
以下のコマンドをターミナルで打ち、ログインしてusers/indexページを開いていきます。
php artisan serve --port=8080
Indexページに、ログインユーザーとの関連性とフォローボタンが設置されていることが確認できます。
↑ボタンのほうもきちんと機能しました。
まとめ&補足
今回は、中間テーブルを作成せずに専用のテーブルを用意しました。
このように作成すると、例えば自分がフォローしている人、自分のフォロワー、相互フォロワーをリスト表示したい場合にとても簡単なので専用テーブルを用意しました。
リストを表示方法も以下に示してみます。
まずはリスト表示するページのルーティングを設定します。UserコントローラーのListファンクションとでも命名します。
routes/web.php
use App\Http\Controllers\UserController; //追加
//------------------- 中略 -------------------
Route::get('/users/list',[UserController::class,'list'])->name('users.list'); //追加
続いて、コントローラの編集してlist機能を追加していきます。
Followモデルを作成したことで、”use App\Models\Follow;
“を宣言することで、Userコントローラ内で直接Followテーブルの操作ができるのでとても便利です。
app/Http/Controllers/UserController.php
use App\Models\Follow; //追加
//------------------------ 中略 ------------------------
public function list(){
//ログインユーザーがフォローしているユーザーのIDを$follow_idにArrayで格納
$follow_id = Follow::where('user_id', Auth::user()->id)->pluck('follow_id')->toArray();
//ログインユーザーをフォローしているユーザーのIDを$follower_idにArrayで格納
$follower_id = Follow::where('follow_id', Auth::user()->id)->pluck('user_id')->toArray();
//相互フォローユーザーのIDを$follow_eachに格納
$follow_each_id = array_intersect($follow_id, $follower_id);
//相互フォローユーザー
$follow_eachs = User::whereIn('id', $follow_each_id)->get();
//ログインユーザーのフォローしているユーザー(相互フォローユーザーを除く)
$follows = User::whereIn('id',$follow_id)->whereNotIn('id',$follow_each_id)->get();
//ログインユーザーをフォローしているユーザー(相互フォローユーザーを除く)
$followers = User::whereIn('id',$follower_id)->whereNotIn('id',$follow_each_id)->get();
return view('users.list', compact('follows','followers','follow_eachs'));
}
ビューを作成し、それを編集してコントローラから受け取った$follows, $followers, $follow_eachsを表示していきます。
#Linuxコマンドで、list.blade.phpを作成
touch resources/views/users/list.blade.php
resources/views/list.blade.php
<div style="margin:30px;">
<h3>相互フォロー</h3>
<table border="1">
<tr>
<th style="padding:10px;">ID</th>
<th style="padding:10px;">名前</th>
</tr>
@foreach($follow_eachs as $follow_each)
<tr>
<td style="padding:10px;">
{{ $follow_each->id }}
</td>
<td style="padding:10px;">
{{ $follow_each->name }}
</td>
</tr>
@endforeach
</table>
<h3>あなたがフォローしているユーザー</h3>
<table border="1">
<tr>
<th style="padding:10px;">ID</th>
<th style="padding:10px;">名前</th>
</tr>
@foreach($follows as $follow)
<tr>
<td style="padding:10px;">
{{ $follow->id }}
</td>
<td style="padding:10px;">
{{ $follow->name }}
</td>
</tr>
@endforeach
</table>
<h3>あなたをフォローしているユーザー</h3>
<table border="1">
<tr>
<th style="padding:10px;">ID</th>
<th style="padding:10px;">名前</th>
</tr>
@foreach($followers as $follower)
<tr>
<td style="padding:10px;">
{{ $follower->id }}
</td>
<td style="padding:10px;">
{{ $follower->name }}
</td>
</tr>
@endforeach
</table>
</div>
/users/listをブラウザで表示すると、以下のように表示されます。