mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
178 lines
5.4 KiB
PHP
178 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace Chill\MainBundle\Search;
|
|
|
|
use Chill\MainBundle\Search\Entity\SearchUserApiProvider;
|
|
use Chill\MainBundle\Serializer\Model\Collection;
|
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
use Chill\PersonBundle\Search\SearchPersonApiProvider;
|
|
use Chill\ThirdPartyBundle\Search\ThirdPartyApiSearch;
|
|
use Doctrine\DBAL\Types\Types;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
|
use Chill\MainBundle\Search\SearchProvider;
|
|
use Symfony\Component\VarDumper\Resources\functions\dump;
|
|
|
|
/**
|
|
*/
|
|
class SearchApi
|
|
{
|
|
private EntityManagerInterface $em;
|
|
private PaginatorFactory $paginator;
|
|
|
|
private array $providers = [];
|
|
|
|
public function __construct(
|
|
EntityManagerInterface $em,
|
|
SearchPersonApiProvider $searchPerson,
|
|
ThirdPartyApiSearch $thirdPartyApiSearch,
|
|
SearchUserApiProvider $searchUser,
|
|
PaginatorFactory $paginator
|
|
)
|
|
{
|
|
$this->em = $em;
|
|
$this->providers[] = $searchPerson;
|
|
$this->providers[] = $thirdPartyApiSearch;
|
|
$this->providers[] = $searchUser;
|
|
$this->paginator = $paginator;
|
|
}
|
|
|
|
/**
|
|
* @return Model/Result[]
|
|
*/
|
|
public function getResults(string $pattern, array $types, array $parameters): Collection
|
|
{
|
|
$queries = $this->findQueries($pattern, $types, $parameters);
|
|
|
|
if (0 === count($queries)) {
|
|
throw new SearchApiNoQueryException($pattern, $types, $parameters);
|
|
}
|
|
|
|
$total = $this->countItems($queries, $types, $parameters);
|
|
$paginator = $this->paginator->create($total);
|
|
|
|
$rawResults = $this->fetchRawResult($queries, $types, $parameters, $paginator);
|
|
|
|
$this->prepareProviders($rawResults);
|
|
$results = $this->buildResults($rawResults);
|
|
|
|
return new Collection($results, $paginator);
|
|
}
|
|
|
|
private function findQueries($pattern, array $types, array $parameters): array
|
|
{
|
|
return \array_map(
|
|
fn($p) => $p->provideQuery($pattern, $parameters),
|
|
$this->findProviders($pattern, $types, $parameters),
|
|
);
|
|
}
|
|
|
|
private function findProviders(string $pattern, array $types, array $parameters): array
|
|
{
|
|
return \array_filter(
|
|
$this->providers,
|
|
fn($p) => $p->supportsTypes($pattern, $types, $parameters)
|
|
);
|
|
}
|
|
|
|
private function countItems($providers, $types, $parameters): int
|
|
{
|
|
list($countQuery, $parameters) = $this->buildCountQuery($providers, $types, $parameters);
|
|
$rsmCount = new ResultSetMappingBuilder($this->em);
|
|
$rsmCount->addScalarResult('count', 'count');
|
|
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
|
$countNq->setParameters($parameters);
|
|
|
|
return $countNq->getSingleScalarResult();
|
|
}
|
|
|
|
private function buildCountQuery(array $queries, $types, $parameters)
|
|
{
|
|
$query = "SELECT COUNT(sq.key) AS count FROM ({union_unordered}) AS sq";
|
|
$unions = [];
|
|
$parameters = [];
|
|
|
|
foreach ($queries as $q) {
|
|
$unions[] = $q->buildQuery();
|
|
$parameters = \array_merge($parameters, $q->buildParameters());
|
|
}
|
|
|
|
$unionUnordered = \implode(" UNION ", $unions);
|
|
|
|
return [
|
|
\strtr($query, [ '{union_unordered}' => $unionUnordered ]),
|
|
$parameters
|
|
];
|
|
}
|
|
|
|
private function buildUnionQuery(array $queries, $types, $parameters)
|
|
{
|
|
$query = "{unions} ORDER BY pertinence DESC";
|
|
$unions = [];
|
|
$parameters = [];
|
|
|
|
foreach ($queries as $q) {
|
|
$unions[] = $q->buildQuery();
|
|
$parameters = \array_merge($parameters, $q->buildParameters());
|
|
}
|
|
|
|
$union = \implode(" UNION ", $unions);
|
|
|
|
return [
|
|
\strtr($query, [ '{unions}' => $union]),
|
|
$parameters
|
|
];
|
|
}
|
|
|
|
private function fetchRawResult($queries, $types, $parameters, $paginator): array
|
|
{
|
|
list($union, $parameters) = $this->buildUnionQuery($queries, $types, $parameters, $paginator);
|
|
$rsm = new ResultSetMappingBuilder($this->em);
|
|
$rsm->addScalarResult('key', 'key', Types::STRING)
|
|
->addScalarResult('metadata', 'metadata', Types::JSON)
|
|
->addScalarResult('pertinence', 'pertinence', Types::FLOAT)
|
|
;
|
|
|
|
$nq = $this->em->createNativeQuery($union, $rsm);
|
|
$nq->setParameters($parameters);
|
|
|
|
return $nq->getResult();
|
|
}
|
|
|
|
private function prepareProviders(array $rawResults)
|
|
{
|
|
$metadatas = [];
|
|
foreach ($rawResults as $r) {
|
|
foreach ($this->providers as $k => $p) {
|
|
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
|
$metadatas[$k][] = $r['metadata'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($metadatas as $k => $m) {
|
|
$this->providers[$k]->prepare($m);
|
|
}
|
|
}
|
|
|
|
private function buildResults(array $rawResults): array
|
|
{
|
|
$items = [];
|
|
|
|
foreach ($rawResults as $r) {
|
|
foreach ($this->providers as $k => $p) {
|
|
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
|
$items[] = (new SearchApiResult($r['pertinence']))
|
|
->setResult(
|
|
$p->getResult($r['key'], $r['metadata'], $r['pertinence'])
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
}
|