mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-14 06:14:23 +00:00
223 lines
7.0 KiB
PHP
223 lines
7.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* Chill is a software for social workers
|
|
*
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Chill\DocStoreBundle\GenericDoc;
|
|
|
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|
use Chill\PersonBundle\Entity\Person;
|
|
use Doctrine\DBAL\Connection;
|
|
use Doctrine\DBAL\Exception;
|
|
use Doctrine\DBAL\Types\Types;
|
|
|
|
final readonly class Manager
|
|
{
|
|
private FetchQueryToSqlBuilder $builder;
|
|
|
|
public function __construct(
|
|
/**
|
|
* @var iterable<GenericDocForAccompanyingPeriodProviderInterface>
|
|
*/
|
|
private iterable $providersForAccompanyingPeriod,
|
|
|
|
/**
|
|
* @var iterable<GenericDocForPersonProviderInterface>
|
|
*/
|
|
private iterable $providersForPerson,
|
|
private Connection $connection,
|
|
) {
|
|
$this->builder = new FetchQueryToSqlBuilder();
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $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<string> $places places to search. When empty, search in all places
|
|
* @return iterable<GenericDocDTO>
|
|
* @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<string> $places places to search. When empty, search in all places
|
|
* @return iterable<GenericDocDTO>
|
|
*/
|
|
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<string> $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];
|
|
}
|
|
}
|