115 lines
5.2 KiB
PHP
115 lines
5.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Studio;
|
|
|
|
use App\Models\User;
|
|
use App\Support\AvatarUrl;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
final class CreatorStudioFollowersService
|
|
{
|
|
public function list(User $user, array $filters = []): array
|
|
{
|
|
$perPage = 30;
|
|
$search = trim((string) ($filters['q'] ?? ''));
|
|
$sort = (string) ($filters['sort'] ?? 'recent');
|
|
$relationship = (string) ($filters['relationship'] ?? 'all');
|
|
$page = max(1, (int) ($filters['page'] ?? 1));
|
|
|
|
$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';
|
|
}
|
|
|
|
$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', fn ($query) => $query->whereNotNull('mutual.created_at'))
|
|
->when($relationship === 'not-followed', fn ($query) => $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, ['*'], 'page', $page)
|
|
->withQueryString();
|
|
|
|
return [
|
|
'items' => collect($followers->items())->map(fn ($row): array => [
|
|
'id' => (int) $row->id,
|
|
'name' => $row->name ?: '@' . $row->username,
|
|
'username' => $row->username,
|
|
'avatar_url' => AvatarUrl::forUser((int) $row->id, $row->avatar_hash, 64),
|
|
'profile_url' => '/@' . strtolower((string) ($row->username ?? $row->id)),
|
|
'uploads_count' => (int) ($row->uploads_count ?? 0),
|
|
'followers_count' => (int) ($row->followers_count ?? 0),
|
|
'is_following_back' => $row->followed_back_at !== null,
|
|
'followed_back_at' => $row->followed_back_at,
|
|
'followed_at' => $row->followed_at,
|
|
])->values()->all(),
|
|
'meta' => [
|
|
'current_page' => $followers->currentPage(),
|
|
'last_page' => $followers->lastPage(),
|
|
'per_page' => $followers->perPage(),
|
|
'total' => $followers->total(),
|
|
],
|
|
'filters' => [
|
|
'q' => $search,
|
|
'sort' => $sort,
|
|
'relationship' => $relationship,
|
|
],
|
|
'summary' => [
|
|
'total_followers' => (clone $summaryBaseQuery)->count(),
|
|
'following_back' => (clone $summaryBaseQuery)->whereNotNull('mutual.created_at')->count(),
|
|
'not_followed' => (clone $summaryBaseQuery)->whereNull('mutual.created_at')->count(),
|
|
],
|
|
'sort_options' => [
|
|
['value' => 'recent', 'label' => 'Most recent'],
|
|
['value' => 'oldest', 'label' => 'Oldest first'],
|
|
['value' => 'name', 'label' => 'Name A-Z'],
|
|
['value' => 'uploads', 'label' => 'Most uploads'],
|
|
['value' => 'followers', 'label' => 'Most followers'],
|
|
],
|
|
'relationship_options' => [
|
|
['value' => 'all', 'label' => 'All followers'],
|
|
['value' => 'following-back', 'label' => 'Following back'],
|
|
['value' => 'not-followed', 'label' => 'Not followed yet'],
|
|
],
|
|
];
|
|
}
|
|
} |