Add follow system, activity feed, notifications, sitemap, caching, Scout search

This commit is contained in:
Mark
2026-05-30 11:23:24 +03:00
parent 5c6ba86e15
commit 3083071a6c
24 changed files with 1657 additions and 49 deletions
@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use App\Models\Activity;
class ActivityController extends Controller
{
public function index()
{
$user = auth()->user();
$followingIds = $user->following()->pluck('users.id')->push($user->id);
$activities = Activity::with(['user', 'subject'])
->whereIn('user_id', $followingIds)
->latest('created_at')
->paginate(40);
return view('activity.index', compact('activities'));
}
public function global()
{
$activities = Activity::with(['user', 'subject'])
->latest('created_at')
->paginate(40);
return view('activity.index', compact('activities'));
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;
class FollowController extends Controller
{
public function store(User $user): JsonResponse
{
$me = auth()->user();
if ($me->id === $user->id) {
return response()->json(['error' => 'Cannot follow yourself'], 422);
}
if (!$me->following()->where('following_id', $user->id)->exists()) {
$me->following()->attach($user->id);
$user->notifications()->create([
'id' => Str::uuid(),
'type' => 'new_follower',
'data' => json_encode([
'follower_id' => $me->id,
'follower_username' => $me->username,
'follower_name' => $me->name,
]),
'created_at' => now(),
]);
}
return response()->json([
'following' => true,
'count' => $user->followers()->count(),
]);
}
public function destroy(User $user): JsonResponse
{
auth()->user()->following()->detach($user->id);
return response()->json([
'following' => false,
'count' => $user->followers()->count(),
]);
}
}
+33 -27
View File
@@ -6,6 +6,7 @@ use App\Models\Game;
use App\Models\Genre;
use App\Models\Platform;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class GameController extends Controller
{
@@ -53,7 +54,10 @@ class GameController extends Controller
$userLists = auth()->user()->lists()->get(['id', 'title']);
}
return view('games.show', compact('game', 'userGame', 'userReview', 'userLists'));
$response = view('games.show', compact('game', 'userGame', 'userReview', 'userLists'));
return auth()->check()
? $response
: response($response)->header('Cache-Control', 'public, max-age=300');
}
public function search(Request $request)
@@ -62,23 +66,19 @@ class GameController extends Controller
$games = collect();
if (strlen($q) >= 2) {
$games = Game::published()
->where('title', 'like', "%{$q}%")
->orderByDesc('rating_count')
->limit(20)
->get();
$games = Game::search($q)->get()->filter(fn($g) => $g->status === 'published');
}
if ($request->expectsJson()) {
return response()->json($games->map(fn($g) => [
'id' => $g->id,
'title' => $g->title,
'slug' => $g->slug,
'cover_thumb' => $g->cover_thumb_url,
'release_year' => $g->release_year,
'rating_avg' => $g->rating_avg,
'url' => route('games.show', $g),
]));
return response()->json($games->take(6)->map(fn($g) => [
'id' => $g->id,
'title' => $g->title,
'slug' => $g->slug,
'cover_thumb' => $g->cover_thumb_url,
'release_year' => $g->release_year,
'rating_avg' => $g->rating_avg,
'url' => route('games.show', $g),
])->values());
}
return view('games.search', compact('games', 'q'));
@@ -86,24 +86,30 @@ class GameController extends Controller
public function topRated(Request $request)
{
$games = Game::published()
->where('rating_count', '>=', 1)
->orderByDesc('rating_avg')
->orderByDesc('rating_count')
->paginate(24)
->withQueryString();
$page = $request->get('page', 1);
$games = Cache::remember("games.top_rated.{$page}", 300, fn() =>
Game::published()
->where('rating_count', '>=', 1)
->orderByDesc('rating_avg')
->orderByDesc('rating_count')
->paginate(24)
->withQueryString()
);
return view('games.top-rated', compact('games'));
}
public function newReleases(Request $request)
{
$games = Game::published()
->whereNotNull('release_date')
->where('release_date', '<=', now())
->orderByDesc('release_date')
->paginate(24)
->withQueryString();
$page = $request->get('page', 1);
$games = Cache::remember("games.new_releases.{$page}", 300, fn() =>
Game::published()
->whereNotNull('release_date')
->where('release_date', '<=', now())
->orderByDesc('release_date')
->paginate(24)
->withQueryString()
);
return view('games.new-releases', compact('games'));
}
+17 -12
View File
@@ -3,24 +3,29 @@
namespace App\Http\Controllers;
use App\Models\Game;
use Illuminate\Support\Facades\Cache;
class HomeController extends Controller
{
public function index()
{
$topRated = Game::published()
->where('rating_count', '>=', 1)
->orderByDesc('rating_avg')
->orderByDesc('rating_count')
->limit(12)
->get();
$topRated = Cache::remember('home.top_rated', 300, fn() =>
Game::published()
->where('rating_count', '>=', 1)
->orderByDesc('rating_avg')
->orderByDesc('rating_count')
->limit(12)
->get()
);
$newReleases = Game::published()
->whereNotNull('release_date')
->where('release_date', '<=', now())
->orderByDesc('release_date')
->limit(8)
->get();
$newReleases = Cache::remember('home.new_releases', 300, fn() =>
Game::published()
->whereNotNull('release_date')
->where('release_date', '<=', now())
->orderByDesc('release_date')
->limit(8)
->get()
);
return view('home', compact('topRated', 'newReleases'));
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers;
use App\Models\Notification;
class NotificationController extends Controller
{
public function index()
{
$notifications = auth()->user()
->notifications()
->limit(20)
->get();
auth()->user()->notifications()->whereNull('read_at')->update(['read_at' => now()]);
return view('notifications.index', compact('notifications'));
}
public function markRead(Notification $notification)
{
abort_if($notification->user_id !== auth()->id(), 403);
$notification->update(['read_at' => now()]);
return response()->json(['ok' => true]);
}
}