📲
Laravelでフォロー機能を実装する方法
Laravel でフォロー機能を API 経由で実装し、JavaScript でフォローボタンを制御する方法を解説します。
実行環境
PHP 8.3
Laravel 11
MySQL 8.0
JavaScript (fetch API使用)
上記を使用してフォロー機能を実装していきます。
また、サイトはログインしなければ使用できない仕様になっています。
フォロー処理の流れ
- ページにアクセス時にフォロー状況をチェック
- フォローしていなければ「フォローボタン」を表示。
- フォローしていれば「フォロー解除ボタン」を表示。
- フォローボタンを押すと API を呼び出してフォロー
- フォロー解除ボタンを押すと API を呼び出してフォロー解除
- ボタンの状態を動的に切り替え
- フォロー後は「フォロー解除」ボタンを表示。
- フォロー解除後は「フォロー」ボタンを表示。
使用したコード
blade(一部抜粋)
laravelのフォロー、フォロー解除ボタンがあるファイルです。
<div class="follow-action">
<button id="follow" class="follow-button {{ $isFollow->value == 'following' ? 'hidden' : '' }}" >
フォロー
</button>
<button id="unfollow" class="unfollow-button {{ $isFollow->value == 'not_following' ? 'hidden' : '' }}">
フォロー中
</button>
</div>
<script>
const loggedInUserId = @json(auth()->id()); //ログイン中のユーザーのidをjsに渡す
const followTargetUserId = @json($material->posts[0]->user->id); //フォローするユーザーをjsに渡す
const isFollow = @json($isFollow); //フォロー状況をjsに渡す
</script>
api.php
apiのルーティングが記載されているファイルです。
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FollowController;
Route::post('/follow/{loggedInUserId}/{followUser}', [FollowController::class, 'follow'])->name('api.follow');
Route::post('/unfollow/{loggedInUserId}/{followUser}', [FollowController::class, 'unfollow'])->name('api.unfollow');
followController.php
フォロー、フォロー解除メソッドが記載されているファイルです。
<?php
namespace App\Http\Controllers;
use App\Services\FollowService;
class FollowController extends Controller
{
protected $followService;
public function __construct(FollowService $followService)
{
$this->followService = $followService;
}
public function follow($loggedInUserId, $followUserId)
{
$result = $this->followService->follow($loggedInUserId, $followUserId);
return response()->json($result);
}
public function unfollow($loggedInUserId, $followUserId)
{
$result = $this->followService->unfollow($loggedInUserId, $followUserId);
return response()->json($result);
}
}
FollowService.php
コントローラーの肥大化を防ぐためにサービスを使用します。
<?php
namespace App\Services;
use App\Models\User_follow;
use Illuminate\Support\Facades\Auth;
use Exception;
class FollowService
{
public function follow($loggedInUserId, $followUserId)
{
if ($loggedInUserId == $followUserId) {
return ['error' => "自分自身をフォローすることはできません"];
}
if (User_follow::where('follower_id', $loggedInUserId)->where('following_id', $followUserId)->exists()) {
return ['error' => "すでにフォロー済みです"];
}
try {
User_follow::create([
'follower_id' => $loggedInUserId,
'following_id' => $followUserId
]);
return [
'success' => true, // 追加
'message' => 'フォロー処理成功'
];
} catch (\Exception $e) {
return ['error' => "フォロー処理に失敗しました", 'details' => $e->getMessage()];
}
}
public function unfollow($loggedInUserId, $followUserId)
{
// フォロー状態を取得
$existingFollow = User_follow::where('follower_id', $loggedInUserId)
->where('following_id', $followUserId)
->first();
try {
if ($existingFollow) {
// フォロー解除(レコード削除)
$existingFollow->delete();
return [
'success' => true,
'message' => 'フォロー解除成功'
];
} else {
// すでにフォローしていない場合
return [
'success' => false,
'message' => 'フォローしていません'
];
}
} catch (Exception $e) {
return [
'error' => "フォロー解除に失敗しました",
'details' => $e->getMessage()
];
}
}
}
JavaScript
このファイルにAPIの処理を記載しています。
document.addEventListener("DOMContentLoaded", function () {
const followButton = document.getElementById("follow");
const unfollowButton = document.getElementById("unfollow");
if (!followButton) return;
followButton.addEventListener("click", function () {
if (!loggedInUserId) {
console.error("ログインユーザーIDが取得できません");
return;
}
fetch(`/api/follow/${loggedInUserId}/${followTargetUserId}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content")
},
credentials: "include"
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log("API Response:", data);
if (data.success) {
followButton.classList.add("hidden"); // フォローボタンを非表示
unfollowButton.classList.remove("hidden"); // フォロー解除ボタンを表示
} else {
alert("エラーが発生しました");
}
})
.catch(error => console.error("Error:", error));
});
if (!unfollowButton) return;
unfollowButton.addEventListener("click", function () {
if (!loggedInUserId) {
console.error("ログインユーザーIDが取得できません");
return;
}
fetch(`/api/unfollow/${loggedInUserId}/${followTargetUserId}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content")
},
credentials: "include"
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log("API Response:", data);
if (data.success) {
unfollowButton.classList.add("hidden"); // フォローボタンを非表示
followButton.classList.remove("hidden"); // フォロー解除ボタンを表示
} else {
alert("エラーが発生しました");
}
})
.catch(error => console.error("Error:", error));
});
});
データベース
テーブルは以下のSQL文で作成しました。
CREATE TABLE user_follows (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
follower_id BIGINT UNSIGNED NOT NULL,
following_id BIGINT UNSIGNED NOT NULL,
FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (following_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE KEY (follower_id, following_id)
);
フォローの動作
bladeのフォローボタンを押したら、APIを使用してコントローラーを介してフォローする人とフォローされる人をデーターベースに登録します。
そして、成功すると処理成功と返します。失敗すると処理失敗を返します。
フォロー解除の動作
フォロー解除ボタン(フォロー中ボタン)を押したら、フォロー同様にAPIを使用してコントローラーを介してフォローする人とフォローされる人をデーターベースに登録します。
そして、成功すると処理成功と返します。失敗すると処理失敗を返します。
おわりに
今回はフォロー機能を実装しました。
X(Twitter)などでフォローするとページ遷移を行わずに実行できます、それにならいAPIを使用して実装しました。また、コードは多くないですが、今後の保守・運用のことを考えてサービスを使用しました。
今後実装していく際にもこれからの保守・運用性のことを考えて実装していきたいと思います。
Discussion