diff --git a/module/Core/src/ShortUrl/Entity/ShortUrl.php b/module/Core/src/ShortUrl/Entity/ShortUrl.php index 6f4b59c6..35c0dfd2 100644 --- a/module/Core/src/ShortUrl/Entity/ShortUrl.php +++ b/module/Core/src/ShortUrl/Entity/ShortUrl.php @@ -264,7 +264,13 @@ class ShortUrl extends AbstractEntity return true; } - public function toArray(?VisitsSummary $precalculatedSummary = null): array + /** + * @param null|(callable(): string|null) $getAuthority - + * This is a callback so that we trust its return value if provided, even if it is null. + * Providing the raw authority as `string|null` would result in a fallback to `$this->domain` when the authority + * was null. + */ + public function toArray(?VisitsSummary $precalculatedSummary = null, callable|null $getAuthority = null): array { return [ 'shortCode' => $this->shortCode, @@ -276,7 +282,7 @@ class ShortUrl extends AbstractEntity 'validUntil' => $this->validUntil?->toAtomString(), 'maxVisits' => $this->maxVisits, ], - 'domain' => $this->domain, + 'domain' => $getAuthority !== null ? $getAuthority() : $this->domain?->authority, 'title' => $this->title, 'crawlable' => $this->crawlable, 'forwardQuery' => $this->forwardQuery, diff --git a/module/Core/src/ShortUrl/Model/ShortUrlWithVisitsSummary.php b/module/Core/src/ShortUrl/Model/ShortUrlWithVisitsSummary.php index 50efaaee..d5c34b8b 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlWithVisitsSummary.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlWithVisitsSummary.php @@ -9,19 +9,26 @@ use Shlinkio\Shlink\Core\Visit\Model\VisitsSummary; final readonly class ShortUrlWithVisitsSummary { - private function __construct(public ShortUrl $shortUrl, private ?VisitsSummary $visitsSummary = null) - { + private function __construct( + public ShortUrl $shortUrl, + private VisitsSummary|null $visitsSummary = null, + private string|null $authority = null, + ) { } /** - * @param array{shortUrl: ShortUrl, visits: string|int, nonBotVisits: string|int} $data + * @param array{shortUrl: ShortUrl, visits: string|int, nonBotVisits: string|int, authority: string|null} $data */ public static function fromArray(array $data): self { - return new self($data['shortUrl'], VisitsSummary::fromTotalAndNonBots( - (int) $data['visits'], - (int) $data['nonBotVisits'], - )); + return new self( + shortUrl: $data['shortUrl'], + visitsSummary: VisitsSummary::fromTotalAndNonBots( + total: (int) $data['visits'], + nonBots: (int) $data['nonBotVisits'], + ), + authority: $data['authority'] ?? null, + ); } public static function fromShortUrl(ShortUrl $shortUrl): self @@ -31,6 +38,6 @@ final readonly class ShortUrlWithVisitsSummary public function toArray(): array { - return $this->shortUrl->toArray($this->visitsSummary); + return $this->shortUrl->toArray($this->visitsSummary, fn() => $this->authority); } } diff --git a/module/Core/src/ShortUrl/Repository/ShortUrlListRepository.php b/module/Core/src/ShortUrl/Repository/ShortUrlListRepository.php index e8fd4ac6..67d85b77 100644 --- a/module/Core/src/ShortUrl/Repository/ShortUrlListRepository.php +++ b/module/Core/src/ShortUrl/Repository/ShortUrlListRepository.php @@ -43,7 +43,7 @@ class ShortUrlListRepository extends EntitySpecificationRepository implements Sh $qb = $this->createListQueryBuilder($filtering); $qb->select( - 'DISTINCT s AS shortUrl', + 'DISTINCT s AS shortUrl, d.authority', '(' . $buildVisitsSubQuery('v', excludingBots: false) . ') AS ' . OrderableField::VISITS->value, '(' . $buildVisitsSubQuery('v2', excludingBots: true) . ') AS ' . OrderableField::NON_BOT_VISITS->value, // This is added only to have a consistent order by title between database engines @@ -89,6 +89,7 @@ class ShortUrlListRepository extends EntitySpecificationRepository implements Sh { $qb = $this->getEntityManager()->createQueryBuilder(); $qb->from(ShortUrl::class, 's') + ->leftJoin('s.domain', 'd') ->where('1=1'); $dateRange = $filtering->dateRange; @@ -129,8 +130,7 @@ class ShortUrlListRepository extends EntitySpecificationRepository implements Sh $conditions[] = $qb->expr()->like('t.name', ':searchPattern'); } - $qb->leftJoin('s.domain', 'd') - ->andWhere($qb->expr()->orX(...$conditions)) + $qb->andWhere($qb->expr()->orX(...$conditions)) ->setParameter('searchPattern', '%' . $searchTerm . '%'); }