Files
SkinbaseNova/app/Services/UserPreferenceService.php
2026-02-27 09:46:51 +01:00

94 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
/**
* UserPreferenceService
*
* Builds a lightweight preference profile for a user based on:
* - Tags on artworks they have favourited
* - Categories of artwork they have favourited / downloaded
* - Creators they follow
*
* Output shape:
* [
* 'top_tags' => ['space', 'nature', ...], // up to 5 slugs
* 'top_categories' => ['wallpapers', ...], // up to 3 slugs
* 'followed_creators' => [1, 5, 23, ...], // user IDs
* ]
*/
final class UserPreferenceService
{
private const CACHE_TTL = 300; // 5 minutes
public function build(User $user): array
{
return Cache::remember(
"user.prefs.{$user->id}",
self::CACHE_TTL,
fn () => $this->compute($user)
);
}
private function compute(User $user): array
{
return [
'top_tags' => $this->topTags($user),
'top_categories' => $this->topCategories($user),
'followed_creators' => $this->followedCreatorIds($user),
];
}
/** Top tag slugs derived from the user's favourited artworks */
private function topTags(User $user, int $limit = 5): array
{
return DB::table('artwork_favourites as af')
->join('artwork_tag as at', 'at.artwork_id', '=', 'af.artwork_id')
->join('tags as t', 't.id', '=', 'at.tag_id')
->where('af.user_id', $user->id)
->where('t.is_active', true)
->selectRaw('t.slug, COUNT(*) as cnt')
->groupBy('t.id', 't.slug')
->orderByDesc('cnt')
->limit($limit)
->pluck('slug')
->values()
->all();
}
/** Top category slugs derived from the user's favourited artworks */
private function topCategories(User $user, int $limit = 3): array
{
return DB::table('artwork_favourites as af')
->join('artwork_category as ac', 'ac.artwork_id', '=', 'af.artwork_id')
->join('categories as c', 'c.id', '=', 'ac.category_id')
->where('af.user_id', $user->id)
->whereNull('c.deleted_at')
->selectRaw('c.slug, COUNT(*) as cnt')
->groupBy('c.id', 'c.slug')
->orderByDesc('cnt')
->limit($limit)
->pluck('slug')
->values()
->all();
}
/** IDs of creators the user follows, latest follows first */
private function followedCreatorIds(User $user, int $limit = 100): array
{
return DB::table('user_followers')
->where('follower_id', $user->id)
->orderByDesc('created_at')
->limit($limit)
->pluck('user_id')
->values()
->all();
}
}