From 3cfef8d07daabd8262c6fe55707a55b74c011361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20P=2E?= Date: Sat, 23 May 2026 22:54:14 +0200 Subject: [PATCH] Add search endpoint for sites (WIP) --- src/Controller/SearchController.php | 28 ++++++++++++ src/Entity/Site.php | 4 ++ src/Enum/OpusEnum.php | 15 +++++++ src/Enum/SitesCategoryEnum.php | 11 +++++ src/Exception/InvalidFilterException.php | 9 ++++ src/Repository/SiteRepository.php | 29 ++++++++++++ src/Service/SearchService.php | 57 ++++++++++++++++++++++++ 7 files changed, 153 insertions(+) create mode 100644 src/Controller/SearchController.php create mode 100644 src/Enum/OpusEnum.php create mode 100644 src/Enum/SitesCategoryEnum.php create mode 100644 src/Exception/InvalidFilterException.php create mode 100644 src/Service/SearchService.php diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php new file mode 100644 index 0000000..ab98cd7 --- /dev/null +++ b/src/Controller/SearchController.php @@ -0,0 +1,28 @@ +getMethod() === 'GET') { + $filters = $request->query->all(); + } + if ($request->getMethod() === 'POST') { + $filters = $request->getPayload(); + } + + $results = $searchService->searchSites($filters); + + return $this->json($results); + } +} diff --git a/src/Entity/Site.php b/src/Entity/Site.php index 8d3be92..44ed234 100644 --- a/src/Entity/Site.php +++ b/src/Entity/Site.php @@ -6,6 +6,7 @@ use App\Repository\SiteRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\PersistentCollection; use \JsonSerializable; #[ORM\Entity(repositoryClass: SiteRepository::class)] @@ -111,6 +112,9 @@ class Site implements JsonSerializable */ private ?array $bibliography; + #[ORM\OneToMany(targetEntity: BuildingTech::class, mappedBy: 'site')] + private ?PersistentCollection $buildingTechs = null; + public function getId(): ?int { return $this->id; diff --git a/src/Enum/OpusEnum.php b/src/Enum/OpusEnum.php new file mode 100644 index 0000000..fcb6c5a --- /dev/null +++ b/src/Enum/OpusEnum.php @@ -0,0 +1,15 @@ +executeQuery($sql, ['id' => $id]) ->fetchAssociative(); } + + /** + * For the search sites endpoint + * @see SearchService + * @return Site[] Returns an array of Site objects + */ + public function findByFilters(array $filters): array + { + $qb = $this->createQueryBuilder('s'); + + if (!empty($filters['technique'])) { + $qb->leftJoin('s.buildingTechs', 'bt'); + $qb->addSelect('bt'); + $qb->andWhere('bt.technique = :tech'); + $qb->setParameter('tech', $filters['technique']); + } + + if (!empty($filters['text'])) { + $qb->andWhere('LOWER(s.label) LIKE :text OR LOWER(s.denomination) LIKE :text'); + $qb->setParameter('text', $filters['text']); + } + + $qb->orderBy('s.label', 'ASC'); + $query = $qb->getQuery(); + + //dd($query, $filters['text']); + + return $query->getResult(); + } // /** // * @return Site[] Returns an array of Site objects diff --git a/src/Service/SearchService.php b/src/Service/SearchService.php new file mode 100644 index 0000000..ce81dd0 --- /dev/null +++ b/src/Service/SearchService.php @@ -0,0 +1,57 @@ +normalizeFilters($rawFilters); + + // No results should be returned if no filter is valid + if (empty($filters)) return []; + + return array_map( + fn(\App\Entity\Site $s) => $s->toSummary(), + $this->siteRepository->findByFilters($filters) + ); + } + /** + * @throws InvalidFilterException + */ + private function normalizeFilters(array $filters): array + { + $allowedFilters = ['text', 'technique', 'category']; + $filters = array_intersect_key($filters, array_flip($allowedFilters)); + + $text = trim($filters['text'] ?? ''); + + if ($text !== '' && strlen($text) < 3) { + throw new InvalidFilterException("Invalid text filter, length: " . strlen($filters['text'])); + } + + // Prepare for LIKE query (useful?) + $filters['text'] = $text !== '' ? '%' . u($text)->lower()->toUnicodeString() . '%' : ''; + + if (!empty($filters['technique']) && OpusEnum::tryFrom($filters['technique']) === null) { + throw new InvalidFilterException("Invalid technique filter: " . $filters['technique']); + } + + if (!empty($filters['category']) && SitesCategoryEnum::tryFrom($filters['category']) === null) { + throw new InvalidFilterException("Invalid category filter: " . $filters['category']); + } + + return $filters; + } +} \ No newline at end of file