add filter for generic doc + fix issues in filter

This commit is contained in:
Julien Fastré 2023-05-30 12:46:05 +02:00
parent a3d3588b75
commit eb107f5a15
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
10 changed files with 109 additions and 25 deletions

View File

@ -14,6 +14,7 @@ namespace Chill\DocStoreBundle\Controller;
use Chill\DocStoreBundle\GenericDoc\Manager;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpFoundation\Response;
@ -25,9 +26,10 @@ use Symfony\Component\Templating\EngineInterface;
final readonly class GenericDocForAccompanyingPeriodController
{
public function __construct(
private Security $security,
private FilterOrderHelperFactory $filterOrderHelperFactory,
private Manager $manager,
private PaginatorFactory $paginator,
private Security $security,
private EngineInterface $twig,
) {
}
@ -45,12 +47,41 @@ final readonly class GenericDocForAccompanyingPeriodController
throw new AccessDeniedHttpException("not allowed to see the documents for accompanying period");
}
$nb = $this->manager->countDocForAccompanyingPeriod($accompanyingPeriod);
$filterBuilder = $this->filterOrderHelperFactory
->create(self::class)
->addSearchBox()
->addDateRange('dateRange', 'generic_doc.filter.date-range');
if ([] !== $places = $this->manager->placesForAccompanyingPeriod($accompanyingPeriod)) {
$filterBuilder->addCheckbox('places', $places, [], array_map(
static fn (string $k) => 'generic_doc.filter.keys.' . $k,
$places
));
}
$filter = $filterBuilder
->build();
['to' => $endDate, 'from' => $startDate ] = $filter->getDateRangeData('dateRange');
$content = $filter->getQueryString();
$nb = $this->manager->countDocForAccompanyingPeriod(
$accompanyingPeriod,
$startDate,
$endDate,
$content,
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
);
$paginator = $this->paginator->create($nb);
$documents = $this->manager->findDocForAccompanyingPeriod(
$accompanyingPeriod,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage()
$paginator->getItemsPerPage(),
$startDate,
$endDate,
$content,
$filter->hasCheckBox('places') ? array_values($filter->getCheckboxData('places')) : []
);
return new Response($this->twig->render(
@ -59,6 +90,7 @@ final readonly class GenericDocForAccompanyingPeriodController
'accompanyingCourse' => $accompanyingPeriod,
'pagination' => $paginator,
'documents' => iterator_to_array($documents),
'filter' => $filter,
]
));
}

View File

@ -14,12 +14,12 @@ namespace Chill\DocStoreBundle\GenericDoc;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
class GenericDocDTO
final readonly class GenericDocDTO
{
public function __construct(
public readonly string $key,
public readonly array $identifiers,
public readonly \DateTimeImmutable $docDate,
public string $key,
public array $identifiers,
public \DateTimeImmutable $docDate,
public AccompanyingPeriod|Person $linked,
) {
}

View File

@ -32,6 +32,7 @@ class Manager
}
/**
* @param list<string> $places
* @throws Exception
*/
public function countDocForAccompanyingPeriod(
@ -39,16 +40,16 @@ class Manager
?\DateTimeImmutable $startDate = null,
?\DateTimeImmutable $endDate = null,
?string $content = null,
?string $origin = null
array $places = []
): int {
['sql' => $sql, 'params' => $params] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $origin);
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
if ($sql === '') {
return 0;
}
$countSql = "SELECT count(*) AS c FROM ({$sql}) AS sq";
$result = $this->connection->executeQuery($countSql, $params);
$result = $this->connection->executeQuery($countSql, $params, $types);
$number = $result->fetchOne();
@ -60,6 +61,7 @@ class Manager
}
/**
* @param list<string> $places places to search. When empty, search in all places
* @throws Exception
*/
public function findDocForAccompanyingPeriod(
@ -69,15 +71,19 @@ class Manager
?\DateTimeImmutable $startDate = null,
?\DateTimeImmutable $endDate = null,
?string $content = null,
?string $origin = null
array $places = []
): iterable {
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $origin);
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod, $startDate, $endDate, $content, $places);
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) {
foreach ($this->connection->iterateAssociative($runSql, $runParams, $runTypes) as $row) {
yield new GenericDocDTO(
$row['key'],
json_decode($row['identifiers'], true, 512, JSON_THROW_ON_ERROR),
@ -87,14 +93,34 @@ class Manager
}
}
public function placesForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): array
{
['sql' => $sql, 'params' => $params, 'types' => $types] = $this->buildUnionQuery($accompanyingPeriod);
if ($sql === '') {
return [];
}
$runSql = "SELECT DISTINCT key FROM ({$sql}) AS sq";
$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,
?string $origin = null
array $places = [],
): array {
$sql = [];
$params = [];
@ -105,17 +131,20 @@ class Manager
if (!$provider->isAllowedForAccompanyingPeriod($linked)) {
continue;
}
$query = $provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content);
['sql' => $q, 'params' => $p, 'types' => $t ] = $this->builder
->toSql($provider->buildFetchQueryForAccompanyingPeriod($linked, $startDate, $endDate, $content, $origin));
$params = [...$params, ...$p];
$types = [...$types, ...$t];
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];
}
}

View File

@ -50,7 +50,7 @@ final readonly class AccompanyingProviderCourseDocumentGenericDoc implements Gen
if (null !== $startDate) {
$query->addWhereClause(
sprintf('? >= %s', $classMetadata->getColumnName('date')),
sprintf('? <= %s', $classMetadata->getColumnName('date')),
[$startDate],
[Types::DATE_IMMUTABLE]
);
@ -58,7 +58,7 @@ final readonly class AccompanyingProviderCourseDocumentGenericDoc implements Gen
if (null !== $endDate) {
$query->addWhereClause(
sprintf('? < %s', $classMetadata->getColumnName('date')),
sprintf('? >= %s', $classMetadata->getColumnName('date')),
[$endDate],
[Types::DATE_IMMUTABLE]
);
@ -71,7 +71,7 @@ final readonly class AccompanyingProviderCourseDocumentGenericDoc implements Gen
$classMetadata->getColumnName('title'),
$classMetadata->getColumnName('description')
),
[$content, $content],
['%' . $content . '%', '%' . $content . '%'],
[Types::STRING, Types::STRING]
);
}

View File

@ -22,6 +22,8 @@
<div class="document-list">
<h1>{{ 'Documents' }}</h1>
{{ filter|chill_render_filter_order_helper }}
{% if documents|length == 0 %}
<p class="chill-no-data-statement">{{ 'No documents'|trans }}</p>
{% else %}

View File

@ -18,6 +18,7 @@ use Chill\DocStoreBundle\GenericDoc\Manager;
use Chill\DocStoreBundle\GenericDoc\GenericDocForAccompanyingPeriodProviderInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
@ -86,12 +87,16 @@ final readonly class SimpleGenericDocProvider implements GenericDocForAccompanyi
{
public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null, ?string $origin = null): FetchQueryInterface
{
return new FetchQuery(
$query = new FetchQuery(
'accompanying_course_document',
sprintf('jsonb_build_object(\'id\', %s)', 'id'),
'd',
'(VALUES (1, \'2023-05-01\'::date), (2, \'2023-05-01\'::date)) AS sq (id, d)',
);
$query->addWhereClause('d > ?', [new \DateTimeImmutable('2023-01-01')], [Types::DATE_IMMUTABLE]);
return $query;
}
public function isAllowedForAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod): bool

View File

@ -22,6 +22,12 @@ Any description: Aucune description
document:
Any title: Aucun titre
generic_doc:
filter:
keys:
accompanying_course_document: Document du parcours
date-range: Date du document
# delete
Delete document ?: Supprimer le document ?
Are you sure you want to remove this document ?: Êtes-vous sûr·e de vouloir supprimer ce document ?

View File

@ -91,6 +91,11 @@ class FilterOrderHelper
return $this->checkboxes;
}
public function hasCheckBox(string $name): bool
{
return array_key_exists($name, $this->checkboxes);
}
/**
* @return array<'to': DateTimeImmutable, 'from': DateTimeImmutable>
*/

View File

@ -71,7 +71,7 @@ final readonly class AccompanyingPeriodWorkEvaluationGenericDocProvider implemen
if (null !== $startDate) {
$query->addWhereClause(
sprintf('doc_store.%s <= ?', $storedObjectMetadata->getColumnName('createdAt')),
sprintf('doc_store.%s >= ?', $storedObjectMetadata->getColumnName('createdAt')),
[$startDate],
[Types::DATE_IMMUTABLE]
);
@ -79,7 +79,7 @@ final readonly class AccompanyingPeriodWorkEvaluationGenericDocProvider implemen
if (null !== $endDate) {
$query->addWhereClause(
sprintf('doc_store.%s > ?', $storedObjectMetadata->getColumnName('createdAt')),
sprintf('doc_store.%s < ?', $storedObjectMetadata->getColumnName('createdAt')),
[$endDate],
[Types::DATE_IMMUTABLE]
);

View File

@ -1231,3 +1231,8 @@ social_action:
social_issue:
and children: et dérivés
generic_doc:
filter:
keys:
accompanying_period_work_evaluation_document: Document des actions d'accompagnement