瀏覽代碼

ajout de la contrainte par année et refactoring

Vincent GUFFON 3 年之前
父節點
當前提交
c9db456ed1

+ 3 - 0
config/packages/doctrine.yaml

@@ -25,6 +25,9 @@ doctrine:
                     date_time_filter:
                         class: App\Filter\DoctrineFilter\DateTimeFilter
                         enabled: true
+                    year_time_filter:
+                        class: App\Filter\DoctrineFilter\YearTimeFilter
+                        enabled: true
                 connection: default
                 auto_mapping: true
                 mappings:

+ 16 - 0
src/Annotation/YearConstraintAware.php

@@ -0,0 +1,16 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Annotation;
+
+use Attribute;
+
+/**
+ * Classe YearConstraintAware qui gère l'annotation pour le Doctrine filter
+ */
+#[Attribute(Attribute::TARGET_CLASS)]
+final class YearConstraintAware
+{
+    public string $startYearFieldName;
+    public string $endYearFieldName;
+}

+ 12 - 5
src/EventListener/DoctrineFilter/DoctrineFilterListener.php

@@ -3,7 +3,8 @@ declare(strict_types=1);
 
 namespace App\EventListener\DoctrineFilter;
 
-use App\Service\Utils\DateTimeConstraint;
+use App\Service\Constraint\DateTimeConstraint;
+use App\Service\Constraint\YearTimeConstraint;
 use Doctrine\ORM\EntityManagerInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpKernel\Event\RequestEvent;
@@ -17,6 +18,7 @@ class DoctrineFilterListener
     public function __construct(
         private EntityManagerInterface $entityManager,
         private DateTimeConstraint $dateTimeConstraint,
+        private YearTimeConstraint $yearTimeConstraint,
         private Security $security,
         private RequestStack $requestStack
     )
@@ -29,10 +31,15 @@ class DoctrineFilterListener
             // don't do anything if it's not the main request
             return;
         }
-        $filter = $this->entityManager->getFilters()->getFilter('date_time_filter');
-        $filter->setParameter('accessId', $this->security->getUser()?->getId() ?? null);
-        $filter->setParameter('_time_constraint', $this->requestStack->getMainRequest()->get('_time_constraint', true));
+        $dateTimeFilter = $this->entityManager->getFilters()->getFilter('date_time_filter');
+        $dateTimeFilter->setParameter('accessId', $this->security->getUser()?->getId() ?? null);
+        $dateTimeFilter->setParameter('_time_constraint', $this->requestStack->getMainRequest()->get('_time_constraint', true));
+        $dateTimeFilter->setDateTimeConstraint($this->dateTimeConstraint);
 
-        $filter->setDateTimeConstraint($this->dateTimeConstraint);
+
+        $yearTimeFilter = $this->entityManager->getFilters()->getFilter('year_time_filter');
+        $yearTimeFilter->setParameter('accessId', $this->security->getUser()?->getId() ?? null);
+        $yearTimeFilter->setParameter('_time_constraint', $this->requestStack->getMainRequest()->get('_time_constraint', true));
+        $yearTimeFilter->setYearTimeConstraint($this->yearTimeConstraint);
     }
 }

+ 61 - 0
src/Filter/DoctrineFilter/AbstractTimeFilterUtils.php

@@ -0,0 +1,61 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Filter\DoctrineFilter;
+
+use App\Annotation\DateTimeConstraintAware;
+use App\Service\Constraint\DateTimeConstraint;
+use App\Service\Utils\StringsUtils;
+use App\Tests\Filter\DoctrineFilter\DateTimeFilterTest;
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Doctrine\ORM\Query\Filter\SQLFilter;
+
+/**
+ * Classe AbstractTimeFilterUtils fournie des méthodes pour construire les requetes de contrainte de temps
+ */
+abstract class AbstractTimeFilterUtils extends SQLFilter
+{
+    /**
+     * Fonction permettant de construire la requête SQL correspondante aux contraintes
+     * @param array $constraints
+     * @param string $targetTableAlias
+     * @param array $fields
+     * @return string
+     */
+    protected function constructQuery(array $constraints, string $targetTableAlias, array $fields): string{
+        $queryConditionsAND = [];
+        foreach ($constraints as $key => $constraint) {
+            $queryConditionsOR = [];
+            foreach ($constraint as $date => $conditions){
+                foreach ($conditions as $condition){
+                    $arithmetic = $this->getArithmeticValue($condition);
+                    if(!is_null($arithmetic))
+                        $queryConditionsOR[] = sprintf("%s.%s %s '%s'", $targetTableAlias, $fields[$key], $arithmetic, $date);
+                    else
+                        $queryConditionsOR[] = sprintf("%s.%s IS NULL", $targetTableAlias, $fields[$key]);
+                }
+            }
+            if(!empty($queryConditionsOR))
+                $queryConditionsAND[] = sprintf("(%s)", join(' OR ', $queryConditionsOR));
+        }
+
+        return implode(" AND ", $queryConditionsAND);
+    }
+
+    /**
+     * Fonction retournant la valeur arithmétique correspondant à la condition de la contrainte
+     * @param $condition
+     * @return string|null
+     * @see DateTimeFilterTest::testGetArithmeticValue()
+     */
+    protected function getArithmeticValue($condition): string|null{
+        switch ($condition){
+            case DateTimeConstraint::INF : return '<';
+            case DateTimeConstraint::EQUAL :  return '=';
+            case DateTimeConstraint::SUP :  return '>';
+            case DateTimeConstraint::INF + DateTimeConstraint::EQUAL : return '<=';
+            case DateTimeConstraint::SUP + DateTimeConstraint::EQUAL  : return '>=';
+        }
+        return null;
+    }
+}

+ 2 - 47
src/Filter/DoctrineFilter/DateTimeFilter.php

@@ -4,16 +4,14 @@ declare(strict_types=1);
 namespace App\Filter\DoctrineFilter;
 
 use App\Annotation\DateTimeConstraintAware;
-use App\Service\Utils\DateTimeConstraint;
+use App\Service\Constraint\DateTimeConstraint;
 use App\Service\Utils\StringsUtils;
-use App\Tests\Filter\DoctrineFilter\DateTimeFilterTest;
 use Doctrine\ORM\Mapping\ClassMetadata;
-use Doctrine\ORM\Query\Filter\SQLFilter;
 
 /**
  * Classe DateTimeFilter qui définie la requête SQL devant être ajoutée aux Entités possédant l'annotation DateTimeConstraintAware
  */
-final class DateTimeFilter extends SQLFilter
+final class DateTimeFilter extends AbstractTimeFilterUtils
 {
     private DateTimeConstraint $dateTimeConstraint;
 
@@ -50,49 +48,6 @@ final class DateTimeFilter extends SQLFilter
         return $this->constructQuery($constraints, $targetTableAlias, $fields);
     }
 
-    /**
-     * Fonction permettant de construire la requête SQL correspondante aux contraintes
-     * @param array $constraints
-     * @param string $targetTableAlias
-     * @param array $fields
-     * @return string
-     */
-    private function constructQuery(array $constraints, string $targetTableAlias, array $fields): string{
-        $queryConditionsAND = [];
-        foreach ($constraints as $key => $constraint) {
-            $queryConditionsOR = [];
-            foreach ($constraint as $date => $conditions){
-                foreach ($conditions as $condition){
-                    $arithmetic = $this->getArithmeticValue($condition);
-                    if(!is_null($arithmetic))
-                        $queryConditionsOR[] = sprintf("%s.%s %s '%s'", $targetTableAlias, $fields[$key], $arithmetic, $date);
-                    else
-                        $queryConditionsOR[] = sprintf("%s.%s IS NULL", $targetTableAlias, $fields[$key]);
-                }
-            }
-            if(!empty($queryConditionsOR))
-                $queryConditionsAND[] = sprintf("(%s)", join(' OR ', $queryConditionsOR));
-        }
-        return join(" AND ", $queryConditionsAND);
-    }
-
-    /**
-     * Fonction retournant la valeur arithmétique correspondant à la condition de la contrainte
-     * @param $condition
-     * @return string|null
-     * @see DateTimeFilterTest::testGetArithmeticValue()
-     */
-    private function getArithmeticValue($condition): string|null{
-        switch ($condition){
-            case DateTimeConstraint::INF : return '<';
-            case DateTimeConstraint::EQUAL :  return '=';
-            case DateTimeConstraint::SUP :  return '>';
-            case DateTimeConstraint::INF + DateTimeConstraint::EQUAL : return '<=';
-            case DateTimeConstraint::SUP + DateTimeConstraint::EQUAL  : return '>=';
-        }
-        return null;
-    }
-
     /**
      * Permets d'assurer l'injection de dépendance du service DateTimeConstraint
      * @param DateTimeConstraint $dateTimeConstraint

+ 62 - 0
src/Filter/DoctrineFilter/YearTimeFilter.php

@@ -0,0 +1,62 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Filter\DoctrineFilter;
+
+use App\Annotation\DateTimeConstraintAware;
+use App\Annotation\YearConstraintAware;
+use App\Service\Constraint\DateTimeConstraint;
+use App\Service\Constraint\YearTimeConstraint;
+use App\Service\Utils\StringsUtils;
+use Doctrine\ORM\Mapping\ClassMetadata;
+
+/**
+ * Classe DateTimeFilter qui définie la requête SQL devant être ajoutée aux Entités possédant l'annotation DateTimeConstraintAware
+ */
+final class YearTimeFilter extends AbstractTimeFilterUtils
+{
+    private YearTimeConstraint $yearTimeConstraint;
+
+    /**
+     * Méthode surchargée de SQLFilter
+     * @param ClassMetadata $targetEntity
+     * @param string $targetTableAlias
+     * @return string
+     * @throws \Exception
+     */
+    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
+    {
+        if(!$this->hasParameter('_time_constraint')
+            || !boolval(StringsUtils::unquote($this->getParameter('_time_constraint')))
+            || !$this->hasParameter('accessId')
+        )
+            return '';
+
+        $yearConstraintAware = $targetEntity->getReflectionClass()->getAttributes(YearConstraintAware::class)[0] ?? null;
+        $startFieldName = $yearConstraintAware?->getArguments()['startYearFieldName'] ?? null;
+        $endFieldName = $yearConstraintAware?->getArguments()['endYearFieldName'] ?? null;
+        if ($startFieldName === '' || is_null($startFieldName) || $endFieldName === '' || is_null($endFieldName)) {
+            return '';
+        }
+
+        $accessId = intval(StringsUtils::unquote($this->getParameter('accessId')));
+
+        $constraints = $this->yearTimeConstraint->invoke($accessId);
+
+        $fields = [
+            DateTimeConstraint::START_KEY => $startFieldName,
+            DateTimeConstraint::END_KEY => $endFieldName
+        ];
+
+        return $this->constructQuery($constraints, $targetTableAlias, $fields);
+    }
+
+    /**
+     * Permets d'assurer l'injection de dépendance du service YearTimeConstraint
+     * @param YearTimeConstraint $yearTimeConstraint
+     *
+     */
+    public function setYearTimeConstraint(YearTimeConstraint $yearTimeConstraint){
+        $this->yearTimeConstraint = $yearTimeConstraint;
+    }
+}

+ 109 - 0
src/Service/Constraint/AbstractTimeConstraintUtils.php

@@ -0,0 +1,109 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Service\Constraint;
+
+/**
+ * Classe Utils qui fournit de méthodes pour calculer les contraintes de temps
+ */
+abstract class AbstractTimeConstraintUtils
+{
+    const NULL = 0;
+    const INF = 1;
+    const EQUAL = 3;
+    const SUP = 5;
+    const CANCEL_OPERATION = 9;
+    const START_KEY = 'start';
+    const END_KEY = 'end';
+    const NULL_VALUE = 'NULL';
+
+    /**
+     * Retourne true si l'utilisateur veux une période précise
+     * @param $historical
+     * @return bool
+     * @see DateTimeConstraintTest::testHasCustomPeriods()
+     */
+    protected function hasCustomPeriods($historical): bool{
+        return array_key_exists('dateStart', $historical) && $historical['dateStart'] && array_key_exists('dateEnd', $historical) && $historical['dateEnd'];
+    }
+
+    /**
+     * Fonction permettant d'ajouter une nouvelle contrainte de date
+     * @param array $contraints
+     * @param array $newContraint
+     * @return array
+     * @see DateTimeConstraintTest::testAddConstraint()
+     */
+    protected function addConstraint(array $contraints, array $newContraint): array{
+        $contraints = $this->mergeConstraint($contraints,$newContraint,self::START_KEY);
+        $contraints = $this->mergeConstraint($contraints,$newContraint,self::END_KEY);
+        return $contraints;
+    }
+
+    /**
+     * Construit le tableau de contraintes pour une condition (start, end)
+     * @param array $contraints
+     * @param array $newContraint
+     * @param string $key
+     * @return array
+     */
+    protected function mergeConstraint(array $contraints, array $newContraint, string $key): array{
+        if(array_key_exists($key, $newContraint)){
+            foreach ($newContraint[$key] as $dateKey => $arithmeticValue){
+                //Si la date à déjà des conditions
+                if(array_key_exists($dateKey, $contraints[$key])){
+                    //Si la conditions (<, >, =, ...) n'est pas encore appliquée à la date
+                    if(!in_array($arithmeticValue, $contraints[$key][$dateKey]))
+                        $contraints[$key][$dateKey][] = $arithmeticValue;
+                }else{
+                    $contraints[$key][$dateKey] = [$arithmeticValue];
+                }
+            }
+        }
+
+        return $contraints;
+    }
+
+    /**
+     * Nettoyage des contraintes (toutes celles supérieur à la condition cancel et les valeurs null isolées)
+     * @param array $constraints
+     * @return array
+     * @see DateTimeConstraintTest::testCleanConstraints()
+     */
+    protected function cleanConstraints(array $constraints): array{
+        $constraints[self::START_KEY] = $this->filterConstraint($constraints, self::START_KEY);
+        $constraints[self::START_KEY] = $this->clearNull($constraints, self::START_KEY);
+
+        $constraints[self::END_KEY] = $this->filterConstraint($constraints, self::END_KEY);
+        $constraints[self::END_KEY] = $this->clearNull($constraints, self::END_KEY);
+        return $constraints;
+    }
+
+    /**
+     * Pour chaque contraintes appliquées à une date on vérifie qu'on ne dépasse pas la valeur cancel : c'est à dire
+     * la condition qui démontre que toutes les valeurs arithmétiques ont été choisies
+     * @param array $constraints
+     * @param $key
+     * @return array
+     * @see DateTimeConstraintTest::testFilterConstraint()
+     */
+    protected function filterConstraint(array $constraints, string $key): array{
+        return array_filter($constraints[$key], function($constraint){
+            return array_sum($constraint) < self::CANCEL_OPERATION ;
+        });
+    }
+
+    /**
+     * On ne conserve pas les contraintes sur des conditions start et end si ces dernieres ne possède qu'une valeur null :
+     * une valeur null doit obligatoirement s'appliquer avec un OR
+     * @param array $constraints
+     * @param $key
+     * @return array
+     * @see DateTimeConstraintTest::testClearNull()
+     */
+    protected function clearNull(array $constraints, string $key): array{
+        if(count($constraints[$key]) === 1 && array_key_exists(self::NULL_VALUE, $constraints[$key]))
+            $constraints[$key] = [];
+        return $constraints[$key];
+    }
+}

+ 3 - 102
src/Service/Utils/DateTimeConstraint.php → src/Service/Constraint/DateTimeConstraint.php

@@ -1,28 +1,19 @@
 <?php
 declare(strict_types=1);
 
-namespace App\Service\Utils;
+namespace App\Service\Constraint;
 
 use App\Entity\Access\Access;
 use App\Service\Organization\Utils as organizationUtils;
-use App\Tests\Service\Utils\DateTimeConstraintTest;
+use App\Tests\Service\Constraint\DateTimeConstraintTest;
 use Doctrine\ORM\EntityManagerInterface;
 
 /**
  * Classe DateTimeConstraint qui définie les dates de débuts et fin de périodes
  * par rapport au contraintes temporelles choisies par un utilisateur.
  */
-class DateTimeConstraint
+class DateTimeConstraint extends AbstractTimeConstraintUtils
 {
-    const NULL = 0;
-    const INF = 1;
-    const EQUAL = 3;
-    const SUP = 5;
-    const CANCEL_OPERATION = 9;
-    const START_KEY = 'start';
-    const END_KEY = 'end';
-    const NULL_VALUE = 'NULL';
-
     public function __construct(
         private EntityManagerInterface $entityManager,
     )
@@ -58,16 +49,6 @@ class DateTimeConstraint
         return $this->cleanConstraints($contraints);
     }
 
-    /**
-     * Retourne true si l'utilisateur veux une période précise
-     * @param $historical
-     * @return bool
-     * @see DateTimeConstraintTest::testHasCustomPeriods()
-     */
-    private function hasCustomPeriods($historical): bool{
-        return array_key_exists('dateStart', $historical) && $historical['dateStart'] && array_key_exists('dateEnd', $historical) && $historical['dateEnd'];
-    }
-
     /**
      * Retourne le tableau des périodes custom
      * @param string $dateStart
@@ -103,86 +84,6 @@ class DateTimeConstraint
         return $periods;
     }
 
-    /**
-     * Fonction permettant d'ajouter une nouvelle contrainte de date
-     * @param array $contraints
-     * @param array $newContraint
-     * @return array
-     * @see DateTimeConstraintTest::testAddConstraint()
-     */
-    private function addConstraint(array $contraints, array $newContraint): array{
-        $contraints = $this->mergeConstraint($contraints,$newContraint,self::START_KEY);
-        $contraints = $this->mergeConstraint($contraints,$newContraint,self::END_KEY);
-        return $contraints;
-    }
-
-    /**
-     * Construit le tableau de contraintes pour une condition (start, end)
-     * @param array $contraints
-     * @param array $newContraint
-     * @param string $key
-     * @return array
-     */
-    private function mergeConstraint(array $contraints, array $newContraint, string $key): array{
-        if(array_key_exists($key, $newContraint)){
-            foreach ($newContraint[$key] as $dateKey => $arithmeticValue){
-                //Si la date à déjà des conditions
-                if(array_key_exists($dateKey, $contraints[$key])){
-                    //Si la conditions (<, >, =, ...) n'est pas encore appliquée à la date
-                    if(!in_array($arithmeticValue, $contraints[$key][$dateKey]))
-                        $contraints[$key][$dateKey][] = $arithmeticValue;
-                }else{
-                    $contraints[$key][$dateKey] = [$arithmeticValue];
-                }
-            }
-        }
-
-        return $contraints;
-    }
-
-    /**
-     * Nettoyage des contraintes (toutes celles supérieur à la condition cancel et les valeurs null isolées)
-     * @param array $constraints
-     * @return array
-     * @see DateTimeConstraintTest::testCleanConstraints()
-     */
-    private function cleanConstraints(array $constraints): array{
-        $constraints[self::START_KEY] = $this->filterConstraint($constraints, self::START_KEY);
-        $constraints[self::START_KEY] = $this->clearNull($constraints, self::START_KEY);
-
-        $constraints[self::END_KEY] = $this->filterConstraint($constraints, self::END_KEY);
-        $constraints[self::END_KEY] = $this->clearNull($constraints, self::END_KEY);
-        return $constraints;
-    }
-
-    /**
-     * Pour chaque contraintes appliquées à une date on vérifie qu'on ne dépasse pas la valeur cancel : c'est à dire
-     * la condition qui démontre que toutes les valeurs arithmétiques ont été choisies
-     * @param array $constraints
-     * @param $key
-     * @return array
-     * @see DateTimeConstraintTest::testFilterConstraint()
-     */
-    private function filterConstraint(array $constraints, string $key): array{
-        return array_filter($constraints[$key], function($constraint){
-            return array_sum($constraint) < self::CANCEL_OPERATION ;
-        });
-    }
-
-    /**
-     * On ne conserve pas les contraintes sur des conditions start et end si ces dernieres ne possède qu'une valeur null :
-     * une valeur null doit obligatoirement s'appliquer avec un OR
-     * @param array $constraints
-     * @param $key
-     * @return array
-     * @see DateTimeConstraintTest::testClearNull()
-     */
-    private function clearNull(array $constraints, string $key): array{
-        if(count($constraints[$key]) === 1 && array_key_exists(self::NULL_VALUE, $constraints[$key]))
-            $constraints[$key] = [];
-        return $constraints[$key];
-    }
-
     /**
      * Une période est dans le présent si :
      *  - la date de début est plus petite ou égale (<=) à la date de fin de période à afficher

+ 121 - 0
src/Service/Constraint/YearTimeConstraint.php

@@ -0,0 +1,121 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Service\Constraint;
+
+use App\Entity\Access\Access;
+use App\Service\Organization\Utils as organizationUtils;
+use Doctrine\ORM\EntityManagerInterface;
+
+/**
+ * Classe YearTimeConstraint qui définie l'année de début (et de fin dans le cas d'une période custom)
+ * par rapport au contraintes temporelles choisies par un utilisateur.
+ */
+class YearTimeConstraint extends AbstractTimeConstraintUtils
+{
+    public function __construct(
+        private EntityManagerInterface $entityManager,
+    )
+    { }
+
+    /**
+     * Main méthode
+     * @param int $accessID
+     * @return array
+     * @throws \Exception
+     */
+    public function invoke(int $accessID): array
+    {
+        $access = $this->entityManager->getRepository(Access::class)->find($accessID);
+        $historical = $access->getHistorical();
+
+        $contraints = [
+            self::START_KEY => [],
+            self::END_KEY => []
+        ];
+
+        if($this->hasCustomPeriods($historical)){
+            $periods = $this->getRangeYear($access, $historical['dateStart'], $historical['dateEnd']);
+            $contraints = $this->addConstraint($contraints, $this->customConstraint($periods));
+        }else{
+            $year = $access->getActivityYear();
+            if($historical['present']) $contraints = $this->addConstraint($contraints, $this->presentConstraint($year));
+            if($historical['past']) $contraints = $this->addConstraint($contraints, $this->pastConstraint($year));
+            if($historical['future']) $contraints = $this->addConstraint($contraints, $this->futurConstraint($year));
+        }
+        return $this->cleanConstraints($contraints);
+    }
+
+    /**
+     * Retourne le tableau des années comprise dans la période custom
+     * @param Access $access
+     * @param string $dateStart
+     * @param string $dateEnd
+     * @return string[]
+     */
+    private function getRangeYear(Access $access, string $dateStart, string $dateEnd): array{
+        $organization = $access->getOrganization();
+        return [
+            organizationUtils::START_DATE_KEY => organizationUtils::getActivityYearSwitchDate($organization, new \DateTime($dateStart)),
+            organizationUtils::END_DATE_KEY => organizationUtils::getActivityYearSwitchDate($organization, new \DateTime($dateEnd))
+        ];
+    }
+
+    /**
+     * Une période est dans le présent si :
+     * - l'année de début est égale (=) à l'année à afficher
+     * @param $year
+     * @return array
+     */
+    private function presentConstraint(int $year): array{
+        return [
+          self::START_KEY => [
+              $year => self::EQUAL
+          ]
+        ];
+    }
+
+    /**
+     * Une période est dans le passée si :
+     * - l'année de début est plus petite (<) à l'année à afficher
+     * @param $year
+     * @return array
+     */
+    private function pastConstraint($year): array{
+        return [
+            self::END_KEY => [
+                $year => self::INF
+            ]
+        ];
+    }
+
+    /**
+     * Une période est dans le future si :
+     * - l'année de début est plus grande (>) à l'année à afficher
+     * @param $year
+     * @return array
+     */
+    private function futurConstraint($year): array{
+        return [
+            self::START_KEY => [
+                $year => self::SUP
+            ]
+        ];
+    }
+
+    /**
+     * Une période est dans une contrainte custom si :
+     * - l'année de début est plus grande ou égale (>=) à l'année de départ
+     * - l'année de début est plus petite ou égale (<=) à l'année de fin
+     * @param $year
+     * @return array
+     */
+    private function customConstraint($years): array{
+        return [
+            self::START_KEY => [
+                $years[organizationUtils::START_DATE_KEY]  => self::SUP + self::EQUAL,
+                $years[organizationUtils::END_DATE_KEY] => self::INF + self::EQUAL
+            ]
+        ];
+    }
+}