*/ private iterable $providersForAccompanyingPeriod, /** * @var iterable */ private iterable $providersForPerson, private Connection $connection, ) { $this->builder = new FetchQueryToSqlBuilder(); } /** * @param list $places * @throws Exception */ public function countDocForAccompanyingPeriod( AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, array $places = [] ): int { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places); return $this->countDoc($sql, $params, $types); } private function countDoc(string $sql, array $params, array $types): int { if ($sql === '') { return 0; } $countSql = "SELECT count(*) AS c FROM ({$sql}) AS sq"; $result = $this->connection->executeQuery($countSql, $params, $types); $number = $result->fetchOne(); if (false === $number) { throw new \UnexpectedValueException("number of documents failed to load"); } return $number; } public function countDocForPerson( Person $person, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, array $places = [] ): int { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places); return $this->countDoc($sql, $params, $types); } /** * @param list $places places to search. When empty, search in all places * @return iterable * @throws Exception */ public function findDocForAccompanyingPeriod( AccompanyingPeriod $accompanyingPeriod, int $offset = 0, int $limit = 20, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, array $places = [] ): iterable { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places); return $this->findDocs($accompanyingPeriod, $sql, $params, $types, $offset, $limit); } /** * @throws \JsonException * @throws Exception */ private function findDocs(AccompanyingPeriod|Person $linked, string $sql, array $params, array $types, int $offset, int $limit): iterable { if ($sql === '') { return []; } $runSql = "{$sql} ORDER BY doc_date DESC LIMIT ? OFFSET ?"; $runParams = [...$params, ...[$limit, $offset]]; $runTypes = [...$types, ...[Types::INTEGER, Types::INTEGER]]; foreach ($this->connection->iterateAssociative($runSql, $runParams, $runTypes) as $row) { yield new GenericDocDTO( $row['key'], json_decode($row['identifiers'], true, 512, JSON_THROW_ON_ERROR), new \DateTimeImmutable($row['doc_date']), $linked, ); } } /** * @param list $places places to search. When empty, search in all places * @return iterable */ public function findDocForPerson( Person $person, int $offset = 0, int $limit = 20, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, array $places = [] ): iterable { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person, $startDate, $endDate, $content, $places); return $this->findDocs($person, $sql, $params, $types, $offset, $limit); } public function placesForPerson(Person $person): array { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($person); return $this->places($sql, $params, $types); } public function placesForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): array { ['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod); return $this->places($sql, $params, $types); } private function places(string $sql, array $params, array $types): array { if ($sql === '') { return []; } $runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq ORDER BY key"; $keys = []; foreach ($this->connection->iterateAssociative($runSql, $params, $types) as $k) { $keys[] = $k['key']; } return $keys; } /** * @param list $places places to search. When empty, search in all places */ private function buildUnionQuery( AccompanyingPeriod|Person $linked, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, array $places = [], ): array { $queries = []; if ($linked instanceof AccompanyingPeriod) { foreach ($this->providersForAccompanyingPeriod as $provider) { if (!$provider->isAllowedForAccompanyingPeriod($linked)) { continue; } $queries[] = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content); } } else { foreach ($this->providersForPerson as $provider) { if (!$provider->isAllowedForPerson($linked)) { continue; } $queries[] = $provider->buildFetchQueryForPerson($linked, $startDate, $endDate, $content); } } $sql = []; $params = []; $types = []; foreach ($queries as $query) { if ([] !== $places and !in_array($query->getSelectKeyString(), $places, true)) { continue; } ['sql' => $q, 'params' => $p, 'types' => $t ] = $this->builder->toSql($query); $sql[] = $q; $params = [...$params, ...$p]; $types = [...$types, ...$t]; } return ['sql' => implode(' UNION ', $sql), 'params' => $params, 'types' => $types]; } }