From c37b98cecd2c1b869a17665be7bf3dab1160679d Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 29 Jun 2021 13:53:15 +0200 Subject: [PATCH 01/13] Add a bit more typing to help future development and customizations. --- .../Controller/AbstractCRUDController.php | 31 +++++++++---------- .../CRUD/Controller/ApiController.php | 3 +- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index a1d28483d..55f3bda24 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -10,6 +10,7 @@ use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\CRUD\Resolver\Resolver; +use Doctrine\ORM\QueryBuilder; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -69,7 +70,7 @@ class AbstractCRUDController extends AbstractController * @param Request $request * @return int */ - protected function countEntities(string $action, Request $request, $_format): int + protected function countEntities(string $action, Request $request, string $_format): int { return $this->buildQueryEntities($action, $request) ->select('COUNT(e)') @@ -89,23 +90,23 @@ class AbstractCRUDController extends AbstractController * It returns, by default, a query builder. * */ - protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator) + protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator): QueryBuilder { - $query = $this->buildQueryEntities($action, $request) + $queryBuilder = $this->buildQueryEntities($action, $request) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setMaxResults($paginator->getItemsPerPage()); // allow to order queries and return the new query - return $this->orderQuery($action, $query, $request, $paginator, $_format); + return $this->orderQuery($action, $queryBuilder, $request, $paginator, $_format); } /** * Add ordering fields in the query build by self::queryEntities * */ - protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) + protected function orderQuery(string $action, QueryBuilder $queryBuilder, Request $request, PaginatorInterface $paginator, string $_format): QueryBuilder { - return $query; + return $queryBuilder; } /** @@ -117,12 +118,8 @@ class AbstractCRUDController extends AbstractController * can add some by using the method `customizeQuery`. * * The alias for the entity is "e". - * - * @param string $action - * @param Request $request - * @return QueryBuilder */ - protected function buildQueryEntities(string $action, Request $request) + protected function buildQueryEntities(string $action, Request $request): QueryBuilder { $qb = $this->getDoctrine()->getManager() ->createQueryBuilder() @@ -135,14 +132,14 @@ class AbstractCRUDController extends AbstractController return $qb; } - protected function customizeQuery(string $action, Request $request, $query): void {} + protected function customizeQuery(string $action, Request $request, QueryBuilder $queryBuilder): void {} /** * Get the result of the query */ - protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) + protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): array { - return $query->getQuery()->getResult(); + return $queryBuilder->getQuery()->getResult(); } protected function onPreIndex(string $action, Request $request, string $_format): ?Response @@ -161,7 +158,7 @@ class AbstractCRUDController extends AbstractController /** * method used by indexAction */ - protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response + protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): ?Response { return null; } @@ -169,7 +166,7 @@ class AbstractCRUDController extends AbstractController /** * method used by indexAction */ - protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response + protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, array $entities): ?Response { return null; } @@ -188,7 +185,7 @@ class AbstractCRUDController extends AbstractController /** * called on post fetch entity */ - protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response + protected function onPostFetchEntity(string $action, Request $request, $entity, string $_format): ?Response { return null; } diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php index cab0bcdcb..fd55df30b 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php @@ -305,7 +305,8 @@ class ApiController extends AbstractCRUDController if ($response instanceof Response) { return $response; } - + + // TODO: $entity is never set before if (!isset($entity)) { $entity = ''; } From c25c302466b3625674ae86edc84f7b6975febe11 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 29 Jun 2021 13:54:01 +0200 Subject: [PATCH 02/13] Sort entities recursively. --- .../Controller/SocialIssueApiController.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php index 40d7068ce..4b8d87180 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -4,6 +4,9 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\Pagination\PaginatorInterface; +use Chill\PersonBundle\Entity\SocialWork\SocialIssue; +use Doctrine\ORM\QueryBuilder; +use Generator; use Symfony\Component\HttpFoundation\Request; class SocialIssueApiController extends ApiController @@ -18,4 +21,41 @@ class SocialIssueApiController extends ApiController ); $query->setParameter('now', new \DateTimeImmutable()); } + + protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): array + { + // Create a lazy generator to avoid performance issues. + $generator = $this->buildRecursively( + $queryBuilder->getQuery()->getResult(), + static fn (SocialIssue $socialIssue): iterable => $socialIssue->getChildren() + ); + + // Sadly, we must convert the generator into an array... + // so we lose all the performance gain we could have by using an iterator. + $results = []; + + // Reduce the generator into an array containing once each SocialIssue. + foreach ($generator as $socialIssue) { + // If the current item hasn't been seen yet, add it to the resultset. + $results += [$socialIssue->getId() => $socialIssue]; + } + + return $results; + } + + /** + * @param iterable $iterable + * @param callable(SocialIssue): iterable $childrenAccessor + * + * @return Generator + */ + private function buildRecursively(iterable $iterable, callable $childrenAccessor): Generator + { + foreach ($iterable as $item) + { + yield $item; + + yield from $this->buildRecursively($childrenAccessor($item), $childrenAccessor); + } + } } From 401659748c534d5329a38bb3c7081181c7b43a7c Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 29 Jun 2021 15:23:00 +0200 Subject: [PATCH 03/13] Default entities order: title ASC --- .../Controller/SocialIssueApiController.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php index 4b8d87180..62a708c45 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -5,9 +5,11 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; +use DateTimeImmutable; use Doctrine\ORM\QueryBuilder; use Generator; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; class SocialIssueApiController extends ApiController { @@ -19,7 +21,7 @@ class SocialIssueApiController extends ApiController $query->expr()->isNull('e.desactivationDate') ) ); - $query->setParameter('now', new \DateTimeImmutable()); + $query->setParameter('now', new DateTimeImmutable()); } protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): array @@ -43,6 +45,15 @@ class SocialIssueApiController extends ApiController return $results; } + protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): ?Response + { + $queryBuilder + ->orderBy("GET_JSON_FIELD_BY_KEY(e.title, :locale)", 'ASC') + ->setParameter(':locale', $request->getLocale()); + + return null; + } + /** * @param iterable $iterable * @param callable(SocialIssue): iterable $childrenAccessor From d50d3e0e4b55a154a3c1fbcd96c93921f79d1669 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 30 Jun 2021 08:33:11 +0200 Subject: [PATCH 04/13] Revert "Add a bit more typing to help future development and customizations." This reverts commit 32ce244de551c437d8eef08edeb1fd1906036827. --- .../Controller/AbstractCRUDController.php | 31 ++++++++++--------- .../CRUD/Controller/ApiController.php | 3 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index 55f3bda24..a1d28483d 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -10,7 +10,6 @@ use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorInterface; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\CRUD\Resolver\Resolver; -use Doctrine\ORM\QueryBuilder; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -70,7 +69,7 @@ class AbstractCRUDController extends AbstractController * @param Request $request * @return int */ - protected function countEntities(string $action, Request $request, string $_format): int + protected function countEntities(string $action, Request $request, $_format): int { return $this->buildQueryEntities($action, $request) ->select('COUNT(e)') @@ -90,23 +89,23 @@ class AbstractCRUDController extends AbstractController * It returns, by default, a query builder. * */ - protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator): QueryBuilder + protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator) { - $queryBuilder = $this->buildQueryEntities($action, $request) + $query = $this->buildQueryEntities($action, $request) ->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber()) ->setMaxResults($paginator->getItemsPerPage()); // allow to order queries and return the new query - return $this->orderQuery($action, $queryBuilder, $request, $paginator, $_format); + return $this->orderQuery($action, $query, $request, $paginator, $_format); } /** * Add ordering fields in the query build by self::queryEntities * */ - protected function orderQuery(string $action, QueryBuilder $queryBuilder, Request $request, PaginatorInterface $paginator, string $_format): QueryBuilder + protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format) { - return $queryBuilder; + return $query; } /** @@ -118,8 +117,12 @@ class AbstractCRUDController extends AbstractController * can add some by using the method `customizeQuery`. * * The alias for the entity is "e". + * + * @param string $action + * @param Request $request + * @return QueryBuilder */ - protected function buildQueryEntities(string $action, Request $request): QueryBuilder + protected function buildQueryEntities(string $action, Request $request) { $qb = $this->getDoctrine()->getManager() ->createQueryBuilder() @@ -132,14 +135,14 @@ class AbstractCRUDController extends AbstractController return $qb; } - protected function customizeQuery(string $action, Request $request, QueryBuilder $queryBuilder): void {} + protected function customizeQuery(string $action, Request $request, $query): void {} /** * Get the result of the query */ - protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): array + protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) { - return $queryBuilder->getQuery()->getResult(); + return $query->getQuery()->getResult(); } protected function onPreIndex(string $action, Request $request, string $_format): ?Response @@ -158,7 +161,7 @@ class AbstractCRUDController extends AbstractController /** * method used by indexAction */ - protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): ?Response + protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response { return null; } @@ -166,7 +169,7 @@ class AbstractCRUDController extends AbstractController /** * method used by indexAction */ - protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, array $entities): ?Response + protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response { return null; } @@ -185,7 +188,7 @@ class AbstractCRUDController extends AbstractController /** * called on post fetch entity */ - protected function onPostFetchEntity(string $action, Request $request, $entity, string $_format): ?Response + protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response { return null; } diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php index fd55df30b..cab0bcdcb 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php @@ -305,8 +305,7 @@ class ApiController extends AbstractCRUDController if ($response instanceof Response) { return $response; } - - // TODO: $entity is never set before + if (!isset($entity)) { $entity = ''; } From b41b1346e5152734709edcb320ff3a0ad2949516 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 30 Jun 2021 08:37:49 +0200 Subject: [PATCH 05/13] Remove typing. Request from https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/99 --- .../Controller/SocialIssueApiController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php index 62a708c45..4437955ed 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -24,11 +24,11 @@ class SocialIssueApiController extends ApiController $query->setParameter('now', new DateTimeImmutable()); } - protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): array + protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) { // Create a lazy generator to avoid performance issues. $generator = $this->buildRecursively( - $queryBuilder->getQuery()->getResult(), + $query->getQuery()->getResult(), static fn (SocialIssue $socialIssue): iterable => $socialIssue->getChildren() ); @@ -45,9 +45,9 @@ class SocialIssueApiController extends ApiController return $results; } - protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, QueryBuilder $queryBuilder): ?Response + protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response { - $queryBuilder + $query ->orderBy("GET_JSON_FIELD_BY_KEY(e.title, :locale)", 'ASC') ->setParameter(':locale', $request->getLocale()); From d208a79764280733e1f1a830b6ac09e61562ecd1 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 30 Jun 2021 15:47:38 +0200 Subject: [PATCH 06/13] Add Doctrine extension. --- .../ChillMainExtension.php | 6 ++- .../Hydration/FlatHierarchyEntityHydrator.php | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 6826f854c..1637ee098 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -32,9 +32,8 @@ use Chill\MainBundle\Doctrine\DQL\JsonAggregate; use Chill\MainBundle\Doctrine\DQL\JsonbExistsInArray; use Chill\MainBundle\Doctrine\DQL\Similarity; use Chill\MainBundle\Doctrine\DQL\OverlapsI; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; use Chill\MainBundle\Doctrine\DQL\Replace; +use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; use Chill\MainBundle\Doctrine\Type\NativeDateIntervalType; use Chill\MainBundle\Doctrine\Type\PointType; use Symfony\Component\HttpFoundation\Request; @@ -186,6 +185,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'OVERLAPSI' => OverlapsI::class, ], ], + 'hydrators' => [ + 'chill_flat_hierarchy_list' => FlatHierarchyEntityHydrator::class, + ], ], ], ); diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php new file mode 100644 index 000000000..ed7588f94 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -0,0 +1,48 @@ +buildRecursively( + parent::hydrateAllData(), + static fn($value): iterable => $value->getChildren(), + ); + + $result = []; + + foreach ($generator as $entity) { + // We cannot expect anything from the object entity + // so, we cannot expect it to have a ::getId() method. + // So we use spl_object_hash() instead. + $result += [spl_object_hash($entity) => $entity]; + } + + return array_values($result); + } + + /** + * @param iterable $iterable + * @param callable(SocialIssue): iterable $childrenAccessor + * + * @return Generator + */ + private function buildRecursively(iterable $iterable, callable $childrenAccessor): Generator + { + foreach ($iterable as $item) + { + yield $item; + + yield from $this->buildRecursively($childrenAccessor($item), $childrenAccessor); + } + } +} From e1f8aa5a5ef3c6c75087f4e5589213598b5cddc2 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 30 Jun 2021 15:49:23 +0200 Subject: [PATCH 07/13] Use custom Doctrine hydrator. --- .../Controller/SocialIssueApiController.php | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php index 4437955ed..63deadd03 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -3,11 +3,9 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; +use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; use Chill\MainBundle\Pagination\PaginatorInterface; -use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use DateTimeImmutable; -use Doctrine\ORM\QueryBuilder; -use Generator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -26,23 +24,9 @@ class SocialIssueApiController extends ApiController protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query) { - // Create a lazy generator to avoid performance issues. - $generator = $this->buildRecursively( - $query->getQuery()->getResult(), - static fn (SocialIssue $socialIssue): iterable => $socialIssue->getChildren() - ); - - // Sadly, we must convert the generator into an array... - // so we lose all the performance gain we could have by using an iterator. - $results = []; - - // Reduce the generator into an array containing once each SocialIssue. - foreach ($generator as $socialIssue) { - // If the current item hasn't been seen yet, add it to the resultset. - $results += [$socialIssue->getId() => $socialIssue]; - } - - return $results; + // In order to work, this hydrator only works with + // entities having the field "children" set up. + return $query->getQuery()->getResult(FlatHierarchyEntityHydrator::LIST); } protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response @@ -53,20 +37,4 @@ class SocialIssueApiController extends ApiController return null; } - - /** - * @param iterable $iterable - * @param callable(SocialIssue): iterable $childrenAccessor - * - * @return Generator - */ - private function buildRecursively(iterable $iterable, callable $childrenAccessor): Generator - { - foreach ($iterable as $item) - { - yield $item; - - yield from $this->buildRecursively($childrenAccessor($item), $childrenAccessor); - } - } } From ef6c5870b55cc6025c7ba21651b8b1b5c360c664 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 30 Jun 2021 22:44:43 +0200 Subject: [PATCH 08/13] Prevent multiples sub-queries. --- .../ORM/Hydration/FlatHierarchyEntityHydrator.php | 8 +++++++- .../Controller/SocialIssueApiController.php | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index ed7588f94..d28c7af09 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -15,7 +15,13 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator { $generator = $this->buildRecursively( parent::hydrateAllData(), - static fn($value): iterable => $value->getChildren(), + static function($value): iterable { + $children = $value->getChildren(); + + $children->setInitialized(true); + + return $children; + } ); $result = []; diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php index 63deadd03..c1d251efb 100644 --- a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -6,6 +6,7 @@ use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; use Chill\MainBundle\Pagination\PaginatorInterface; use DateTimeImmutable; +use Doctrine\ORM\Query; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -26,7 +27,10 @@ class SocialIssueApiController extends ApiController { // In order to work, this hydrator only works with // entities having the field "children" set up. - return $query->getQuery()->getResult(FlatHierarchyEntityHydrator::LIST); + return $query + ->getQuery() + ->setHint(Query::HINT_INCLUDE_META_COLUMNS, true) + ->getResult(FlatHierarchyEntityHydrator::LIST); } protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response From b33cb4946c0641c28f0fc4e0a65bb0f3ae96789d Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Thu, 1 Jul 2021 09:12:39 +0200 Subject: [PATCH 09/13] Improve/optimize list generation. --- .../Hydration/FlatHierarchyEntityHydrator.php | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index d28c7af09..0bd9505a6 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -11,44 +11,38 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator { public const LIST = 'chill_flat_hierarchy_list'; - protected function hydrateAllData(): array + protected function hydrateAllData() { - $generator = $this->buildRecursively( - parent::hydrateAllData(), - static function($value): iterable { - $children = $value->getChildren(); - - $children->setInitialized(true); - - return $children; - } - ); - - $result = []; - - foreach ($generator as $entity) { - // We cannot expect anything from the object entity - // so, we cannot expect it to have a ::getId() method. - // So we use spl_object_hash() instead. - $result += [spl_object_hash($entity) => $entity]; - } - - return array_values($result); + return array_values(iterator_to_array($this->flatListGenerator($this->buildChildrenHashmap(parent::hydrateAllData())))); } - /** - * @param iterable $iterable - * @param callable(SocialIssue): iterable $childrenAccessor - * - * @return Generator - */ - private function buildRecursively(iterable $iterable, callable $childrenAccessor): Generator + private function flatListGenerator(array $hashMap, $parent = null): Generator { - foreach ($iterable as $item) - { - yield $item; + $parent = null === $parent ? + null : + spl_object_hash($parent); - yield from $this->buildRecursively($childrenAccessor($item), $childrenAccessor); + $hashMap += [$parent => []]; + + foreach ($hashMap[$parent] as $node) { + yield $node->getId() => $node; + yield from $this->flatListGenerator($hashMap, $node); } } + + private function buildChildrenHashmap(array $nodes): array + { + $r = []; + + foreach ($nodes as $node) { + $parentId = (null !== $parent = $node->getParent()) + ? spl_object_hash($parent) + : null; + + $r[$parentId][] = $node; + } + + return $r; + } + } From cc824faf544b04fa4a31c4410917e47b0677a186 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Sat, 10 Jul 2021 07:50:15 +0200 Subject: [PATCH 10/13] Improve performance. --- .../Hydration/FlatHierarchyEntityHydrator.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index 0bd9505a6..eaa7b6c72 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -16,33 +16,33 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator return array_values(iterator_to_array($this->flatListGenerator($this->buildChildrenHashmap(parent::hydrateAllData())))); } - private function flatListGenerator(array $hashMap, $parent = null): Generator + private function flatListGenerator(array $hashMap, ?int $parent = null): Generator { - $parent = null === $parent ? - null : - spl_object_hash($parent); + $parent ??= spl_object_id($parent); $hashMap += [$parent => []]; foreach ($hashMap[$parent] as $node) { - yield $node->getId() => $node; + yield spl_object_id($node) => $node; yield from $this->flatListGenerator($hashMap, $node); } } private function buildChildrenHashmap(array $nodes): array { - $r = []; + return array_reduce( + $nodes, + static function (array $collect, $node) { + $parentId = (null === $parent = $node->getParent()) ? + null : + spl_object_id($parent); - foreach ($nodes as $node) { - $parentId = (null !== $parent = $node->getParent()) - ? spl_object_hash($parent) - : null; + $collect[$parentId][] = $node; - $r[$parentId][] = $node; - } - - return $r; + return $collect; + }, + [] + ); } } From 53d3a0921ba12a0b0dcbc8d4629f737d84e1a111 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Sat, 10 Jul 2021 08:17:37 +0200 Subject: [PATCH 11/13] Fix minor bug. --- .../Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index eaa7b6c72..91013ddba 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -18,8 +18,7 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator private function flatListGenerator(array $hashMap, ?int $parent = null): Generator { - $parent ??= spl_object_id($parent); - + $parent = null === $parent ? null : spl_object_id($parent); $hashMap += [$parent => []]; foreach ($hashMap[$parent] as $node) { From d0b51f3573336fcad5490fe3e0fb3eae1ffd3add Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Sat, 10 Jul 2021 08:18:00 +0200 Subject: [PATCH 12/13] Add minor typing. --- .../Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index 91013ddba..48b6ff875 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -31,7 +31,7 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator { return array_reduce( $nodes, - static function (array $collect, $node) { + static function (array $collect, $node): array { $parentId = (null === $parent = $node->getParent()) ? null : spl_object_id($parent); From 9b756ffb2f25ddbc92537f7a43f7943e3a5004d7 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 3 Aug 2021 09:24:53 +0200 Subject: [PATCH 13/13] Fix parameter type. --- .../Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php index 48b6ff875..c389b543d 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php +++ b/src/Bundle/ChillMainBundle/Doctrine/ORM/Hydration/FlatHierarchyEntityHydrator.php @@ -16,7 +16,7 @@ final class FlatHierarchyEntityHydrator extends ObjectHydrator return array_values(iterator_to_array($this->flatListGenerator($this->buildChildrenHashmap(parent::hydrateAllData())))); } - private function flatListGenerator(array $hashMap, ?int $parent = null): Generator + private function flatListGenerator(array $hashMap, ?object $parent = null): Generator { $parent = null === $parent ? null : spl_object_id($parent); $hashMap += [$parent => []];