94 lines
2.8 KiB
PHP
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();
|
|
}
|
|
}
|