repository = $entityManager->getRepository($this->getClassName()); $this->entityManager = $entityManager; } public function countByPattern(string $pattern, ?Country $country): int { $query = $this->buildQueryByPattern($pattern, $country); $sql = $query->buildQuery(true); $rsm = new ResultSetMapping(); $rsm->addScalarResult('c', 'c'); $nq = $this->entityManager->createNativeQuery($sql, $rsm) ->setParameters($query->buildParameters(true)); return (int) $nq->getSingleResult()['c']; } public function find($id, $lockMode = null, $lockVersion = null): ?PostalCode { return $this->repository->find($id, $lockMode, $lockVersion); } public function findAll(): array { return $this->repository->findAll(); } public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } public function findByPattern(string $pattern, ?Country $country, ?int $start = 0, ?int $limit = 50): array { $query = $this->buildQueryByPattern($pattern, $country); $rsm = new ResultSetMappingBuilder($this->entityManager); $rsm->addRootEntityFromClassMetadata(PostalCode::class, 'cmpc'); $query->addSelectClause($rsm->generateSelectClause()); $sql = strtr( $query->buildQuery() . 'ORDER BY pertinence DESC, canonical ASC OFFSET ? LIMIT ? ', // little hack for adding sql method to point ['cmpc.center AS center' => 'ST_AsGeojson(cmpc.center) AS center'] ); $parameters = [...$query->buildParameters(), $start, $limit]; return $this->entityManager->createNativeQuery($sql, $rsm) ->setParameters($parameters) ->getResult(); } public function findOneBy(array $criteria, ?array $orderBy = null): ?PostalCode { return $this->repository->findOneBy($criteria, $orderBy); } public function getClassName(): string { return PostalCode::class; } private function buildQueryByPattern(string $pattern, ?Country $country): SearchApiQuery { $pattern = trim($pattern); if ('' === $pattern) { throw new RuntimeException('the search pattern must not be empty'); } $query = new SearchApiQuery(); $query ->setFromClause('chill_main_postal_code cmpc') ->andWhereClause('cmpc.origin = 0'); if (null !== $country) { $query->andWhereClause('cmpc.country_id = ?', [$country->getId()]); } $pertinenceClause = ['STRICT_WORD_SIMILARITY(canonical, UNACCENT(?))']; $pertinenceArgs = [$pattern]; $andWhere = ['canonical %>> UNACCENT(?)']; $andWhereArgs = [$pattern]; foreach (explode(' ', $pattern) as $part) { $part = trim($part); if ('' === $part) { continue; } $andWhere[] = "canonical LIKE '%' || UNACCENT(LOWER(?)) || '%'"; $andWhereArgs[] = $part; $pertinenceClause[] = "(EXISTS (SELECT 1 FROM unnest(string_to_array(canonical, ' ')) AS t WHERE starts_with(t, UNACCENT(LOWER(?)))))::int"; $pertinenceClause[] = '(canonical LIKE UNACCENT(LOWER(?)))::int'; array_push($pertinenceArgs, $part, $part); } $query ->setSelectPertinence(implode(' + ', $pertinenceClause), $pertinenceArgs) ->andWhereClause(implode(' AND ', $andWhere), $andWhereArgs); return $query; } }