user(); $perPage = 30; $search = trim((string) $request->query('q', '')); $sort = (string) $request->query('sort', 'recent'); $relationship = (string) $request->query('relationship', 'all'); $allowedSorts = ['recent', 'oldest', 'name', 'uploads', 'followers']; $allowedRelationships = ['all', 'following-back', 'not-followed']; if (! in_array($sort, $allowedSorts, true)) { $sort = 'recent'; } if (! in_array($relationship, $allowedRelationships, true)) { $relationship = 'all'; } // People who follow $user (user_id = $user being followed) $baseQuery = DB::table('user_followers as uf') ->join('users as u', 'u.id', '=', 'uf.follower_id') ->leftJoin('user_profiles as up', 'up.user_id', '=', 'u.id') ->leftJoin('user_statistics as us', 'us.user_id', '=', 'u.id') ->leftJoin('user_followers as mutual', function ($join) use ($user): void { $join->on('mutual.user_id', '=', 'uf.follower_id') ->where('mutual.follower_id', '=', $user->id); }) ->where('uf.user_id', $user->id) ->whereNull('u.deleted_at') ->when($search !== '', function ($query) use ($search): void { $query->where(function ($inner) use ($search): void { $inner->where('u.username', 'like', '%' . $search . '%') ->orWhere('u.name', 'like', '%' . $search . '%'); }); }) ->when($relationship === 'following-back', function ($query): void { $query->whereNotNull('mutual.created_at'); }) ->when($relationship === 'not-followed', function ($query): void { $query->whereNull('mutual.created_at'); }); $summaryBaseQuery = clone $baseQuery; $followers = $baseQuery ->when($sort === 'recent', fn ($query) => $query->orderByDesc('uf.created_at')) ->when($sort === 'oldest', fn ($query) => $query->orderBy('uf.created_at')) ->when($sort === 'name', fn ($query) => $query->orderByRaw('COALESCE(u.username, u.name) asc')) ->when($sort === 'uploads', fn ($query) => $query->orderByDesc('us.uploads_count')->orderByRaw('COALESCE(u.username, u.name) asc')) ->when($sort === 'followers', fn ($query) => $query->orderByDesc('us.followers_count')->orderByRaw('COALESCE(u.username, u.name) asc')) ->select([ 'u.id', 'u.username', 'u.name', 'up.avatar_hash', 'us.uploads_count', 'us.followers_count', 'uf.created_at as followed_at', 'mutual.created_at as followed_back_at', ]) ->paginate($perPage) ->withQueryString() ->through(fn ($row) => (object) [ 'id' => $row->id, 'name' => $row->name, 'username' => $row->username, 'uname' => $row->username ?? $row->name, 'avatar_url' => AvatarUrl::forUser((int) $row->id, $row->avatar_hash, 64), 'profile_url' => '/@' . strtolower((string) ($row->username ?? $row->id)), 'uploads' => $row->uploads_count ?? 0, 'followers_count' => $row->followers_count ?? 0, 'is_following_back' => $row->followed_back_at !== null, 'followed_back_at' => $row->followed_back_at, 'followed_at' => $row->followed_at, ]); $summary = [ 'total_followers' => (clone $summaryBaseQuery)->count(), 'following_back' => (clone $summaryBaseQuery)->whereNotNull('mutual.created_at')->count(), 'not_followed' => (clone $summaryBaseQuery)->whereNull('mutual.created_at')->count(), ]; return view('dashboard.followers', [ 'followers' => $followers, 'filters' => [ 'q' => $search, 'sort' => $sort, 'relationship' => $relationship, ], 'summary' => $summary, 'page_title' => 'My Followers', ]); } }