88 lines
3.3 KiB
PHP
88 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Livewire\Calls;
|
|
|
|
use App\Models\Call;
|
|
|
|
class CallCarouselScorer
|
|
{
|
|
private array $cfg;
|
|
private ?int $profileCityId;
|
|
private ?int $profileDivisionId;
|
|
private ?int $profileCountryId;
|
|
|
|
private const UNKNOWN_COUNTRY_ID = 10;
|
|
private const REACTION_TYPE_LIKE = 3;
|
|
private const REACTION_TYPE_STAR = 1;
|
|
|
|
public function __construct(
|
|
array $carouselConfig,
|
|
?int $profileCityId,
|
|
?int $profileDivisionId,
|
|
?int $profileCountryId
|
|
) {
|
|
$this->cfg = $carouselConfig;
|
|
$this->profileCityId = $profileCityId;
|
|
$this->profileDivisionId = $profileDivisionId;
|
|
$this->profileCountryId = $profileCountryId;
|
|
}
|
|
|
|
public function score(Call $call): float
|
|
{
|
|
$score = 1.0;
|
|
$loc = $call->location;
|
|
|
|
// --- Location specificity (only best-matching tier applied) ---
|
|
if ($loc) {
|
|
if ($loc->country_id === self::UNKNOWN_COUNTRY_ID) {
|
|
$score *= (float) ($this->cfg['boost_location_unknown'] ?? 0.8);
|
|
} elseif ($loc->city_id) {
|
|
$score *= (float) ($this->cfg['boost_location_city'] ?? 2.0);
|
|
} elseif ($loc->division_id) {
|
|
$score *= (float) ($this->cfg['boost_location_division'] ?? 1.5);
|
|
} elseif ($loc->country_id) {
|
|
$score *= (float) ($this->cfg['boost_location_country'] ?? 1.1);
|
|
}
|
|
|
|
// Same-city (district) proximity bonus
|
|
if ($this->profileCityId && $loc->city_id === $this->profileCityId) {
|
|
$score *= (float) ($this->cfg['boost_same_district'] ?? 3.0);
|
|
}
|
|
}
|
|
|
|
// --- Engagement: likes on the call ---
|
|
$likeCount = $call->loveReactant?->reactionCounters
|
|
->firstWhere('reaction_type_id', self::REACTION_TYPE_LIKE)?->count ?? 0;
|
|
$score *= (1.0 + $likeCount * (float) ($this->cfg['boost_like_count'] ?? 0.05));
|
|
|
|
// --- Engagement: stars on the callable ---
|
|
$starCount = $call->callable?->loveReactant?->reactionCounters
|
|
->firstWhere('reaction_type_id', self::REACTION_TYPE_STAR)?->count ?? 0;
|
|
$score *= (1.0 + $starCount * (float) ($this->cfg['boost_star_count'] ?? 0.10));
|
|
|
|
// --- Recency (created_at) ---
|
|
$recentDays = (int) ($this->cfg['recent_days'] ?? 14);
|
|
if ($call->created_at && $call->created_at->gte(now()->subDays($recentDays))) {
|
|
$score *= (float) ($this->cfg['boost_recent_from'] ?? 1.3);
|
|
}
|
|
|
|
// --- Urgency (till expiry) ---
|
|
$soonDays = (int) ($this->cfg['soon_days'] ?? 7);
|
|
if ($call->till && $call->till->lte(now()->addDays($soonDays))) {
|
|
$score *= (float) ($this->cfg['boost_soon_till'] ?? 1.2);
|
|
}
|
|
|
|
// --- Callable type ---
|
|
$callableType = $call->callable_type ?? '';
|
|
if (str_ends_with($callableType, 'User')) {
|
|
$score *= (float) ($this->cfg['boost_callable_user'] ?? 1.0);
|
|
} elseif (str_ends_with($callableType, 'Organization')) {
|
|
$score *= (float) ($this->cfg['boost_callable_organization'] ?? 1.2);
|
|
} elseif (str_ends_with($callableType, 'Bank')) {
|
|
$score *= (float) ($this->cfg['boost_callable_bank'] ?? 1.0);
|
|
}
|
|
|
|
return $score;
|
|
}
|
|
}
|