Files
SkinbaseNova/.deploy/artwork-evolution-release/app/DTOs/UserRecoProfileDTO.php
2026-04-18 17:02:56 +02:00

95 lines
3.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\DTOs;
/**
* Lightweight value object representing a user's recommendation preference profile.
*
* Built by UserPreferenceBuilder from signals:
* - favourited artworks (+3)
* - awards given (+5)
* - creator follows (+2 for their tags)
* - own uploads (category bias)
*
* Cached in `user_reco_profiles` with a configurable TTL (default 6 hours).
*/
final class UserRecoProfileDTO
{
/**
* @param array<int, string> $topTagSlugs Top tag slugs by weighted score (up to 20)
* @param array<int, string> $topCategorySlugs Top category slugs (up to 5)
* @param array<int, int> $strongCreatorIds Followed creator user IDs (up to 50)
* @param array<string, float> $tagWeights Tag slug → normalised weight (01)
* @param array<string, float> $categoryWeights Category slug → normalised weight
* @param array<int, string> $dislikedTagSlugs Future: blocked/hidden tag slugs
*/
public function __construct(
public readonly array $topTagSlugs = [],
public readonly array $topCategorySlugs = [],
public readonly array $strongCreatorIds = [],
public readonly array $tagWeights = [],
public readonly array $categoryWeights = [],
public readonly array $dislikedTagSlugs = [],
) {}
/**
* True if the user has enough signals to drive personalised recommendations.
*/
public function hasSignals(): bool
{
return $this->topTagSlugs !== [] || $this->strongCreatorIds !== [];
}
/**
* Returns the normalised tag weight for a given slug (0.0 if unknown).
*/
public function tagWeight(string $slug): float
{
return (float) ($this->tagWeights[$slug] ?? 0.0);
}
/**
* Returns true when the creator is in the user's strong-follow list.
*/
public function followsCreator(int $userId): bool
{
return in_array($userId, $this->strongCreatorIds, true);
}
/**
* Serialise for storage in the DB / Redis cache.
*
* @return array<string, mixed>
*/
public function toArray(): array
{
return [
'top_tags' => $this->topTagSlugs,
'top_categories' => $this->topCategorySlugs,
'strong_creators' => $this->strongCreatorIds,
'tag_weights' => $this->tagWeights,
'category_weights' => $this->categoryWeights,
'disliked_tags' => $this->dislikedTagSlugs,
];
}
/**
* Re-hydrate from a stored array (e.g. from the DB JSON column).
*
* @param array<string, mixed> $data
*/
public static function fromArray(array $data): self
{
return new self(
topTagSlugs: (array) ($data['top_tags'] ?? []),
topCategorySlugs: (array) ($data['top_categories'] ?? []),
strongCreatorIds: array_map('intval', (array) ($data['strong_creators'] ?? [])),
tagWeights: array_map('floatval', (array) ($data['tag_weights'] ?? [])),
categoryWeights: array_map('floatval', (array) ($data['category_weights'] ?? [])),
dislikedTagSlugs: (array) ($data['disliked_tags'] ?? []),
);
}
}