mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	add filter for generic doc + fix issues in filter
This commit is contained in:
		| @@ -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, | ||||
|             ] | ||||
|         )); | ||||
|     } | ||||
|   | ||||
| @@ -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, | ||||
|     ) { | ||||
|     } | ||||
|   | ||||
| @@ -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]; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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] | ||||
|             ); | ||||
|         } | ||||
|   | ||||
| @@ -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 %} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 ? | ||||
|   | ||||
| @@ -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> | ||||
|      */ | ||||
|   | ||||
| @@ -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] | ||||
|             ); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user