feat: add multi-city representation support and domain context resolution

- Created migration to add representation_cities table and domain, is_global fields to representations.
- Implemented SiteContextController to resolve domain to site context (city | representation | unknown).
- Developed DomainContext and DomainContextResolver services for domain mapping.
- Added tests for DomainContextResolver and commission logic based on domain ownership.
This commit is contained in:
hamed
2026-07-09 07:26:58 +03:30
parent 59559e2e31
commit 0750bc9812
56 changed files with 4297 additions and 1600 deletions
@@ -0,0 +1,66 @@
<?php
namespace App\Representation\Service;
use App\Location\Repository\CityRepository;
use App\Representation\Entity\Representation;
use App\Representation\Repository\RepresentationRepository;
/**
* تنها نقطه‌ی نگاشت host → context در backend. هیچ سرویس/کنترلر دیگری نباید
* مستقیماً دامنه را parse یا با cities/representations تطبیق دهد.
*
* ورودی هر شکلی از آدرس را می‌پذیرد (host خالص، URL کامل، با www/پورت).
*/
class DomainContextResolver
{
public function __construct(
private readonly CityRepository $cityRepo,
private readonly RepresentationRepository $representationRepo,
) {}
/**
* اعمال scope دامنه روی فیلترهای لیست‌های عمومی (doctors/clinics):
* دامنه‌ی نماینده‌ی سراسری → فقط ردیف‌های همان نماینده؛ فیلتر شهر/استان بی‌اثر.
* دامنه‌ی شهری/ناشناخته → فیلترها دست‌نخورده (فقط کلید domain حذف می‌شود).
*/
public function applyToListFilters(array $filters): array
{
$domain = $filters['domain'] ?? null;
unset($filters['domain']);
if (!is_string($domain) || $domain === '') {
return $filters;
}
$ctx = $this->resolve($domain);
if ($ctx->isGlobalRepresentation) {
$filters['representation_id'] = $ctx->representationId();
unset($filters['city_id'], $filters['state_id'], $filters['city'], $filters['state']);
}
return $filters;
}
public function resolve(?string $hostOrUrl): DomainContext
{
$domain = Representation::normalizeDomain($hostOrUrl);
if ($domain === null) {
return DomainContext::empty();
}
$city = $this->cityRepo->findByDomain($domain);
if ($city !== null) {
// دامنه‌ی شهری؛ نماینده‌ای که دامنه‌ی اختصاصی‌اش این باشد وجود ندارد،
// ولی ممکن است نماینده‌ای دامنه‌ی شهر را به‌عنوان دامنه‌ی خودش ثبت کرده باشد.
$rep = $this->representationRepo->findActiveByDomain($domain);
return new DomainContext($rep, $city, false);
}
$rep = $this->representationRepo->findActiveByDomain($domain);
if ($rep !== null) {
return new DomainContext($rep, null, $rep->isGlobal());
}
return DomainContext::empty();
}
}