From 00e41bc491da0e6cd65fbcba6fe6825bed76caf5 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 20 Jun 2025 00:14:13 +0100 Subject: [PATCH 01/23] IBX-10176 Forward patch Ibexa count modifications --- .../Persistence/Content/Location/Handler.php | 2 +- .../Persistence/Filter/Content/Handler.php | 2 +- .../Persistence/Filter/Location/Handler.php | 2 +- src/contracts/Repository/ContentService.php | 4 +- .../Decorator/ContentServiceDecorator.php | 4 +- .../Decorator/LocationServiceDecorator.php | 12 ++-- src/contracts/Repository/LocationService.php | 8 ++- src/lib/Persistence/Cache/LocationHandler.php | 4 +- .../Legacy/Content/Location/Gateway.php | 2 +- .../Location/Gateway/DoctrineDatabase.php | 11 +++- .../Location/Gateway/ExceptionConversion.php | 4 +- .../Legacy/Content/Location/Handler.php | 4 +- .../Content/Doctrine/DoctrineGateway.php | 11 +++- .../Legacy/Filter/Gateway/Gateway.php | 4 +- .../Location/Doctrine/DoctrineGateway.php | 11 +++- .../Handler/ContentFilteringHandler.php | 4 +- .../Handler/LocationFilteringHandler.php | 4 +- .../Doctrine/LimitedCountQueryTrait.php | 56 +++++++++++++++++++ src/lib/Repository/ContentService.php | 4 +- src/lib/Repository/LocationService.php | 8 +-- .../SiteAccessAware/ContentService.php | 5 +- .../SiteAccessAware/LocationService.php | 13 +++-- .../Core/Repository/LocationServiceTest.php | 56 +++++++++++++++++++ .../LocationServiceDecoratorTest.php | 2 +- 24 files changed, 191 insertions(+), 46 deletions(-) create mode 100644 src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index 5eb76c4c78..b548a38daf 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -110,7 +110,7 @@ public function loadParentLocationsForDraftContent($contentId); */ public function copySubtree($sourceId, $destinationParentId); - public function getSubtreeSize(string $path): int; + public function getSubtreeSize(string $path, ?int $limit = null): int; /** * Moves location identified by $sourceId into new parent identified by $destinationParentId. diff --git a/src/contracts/Persistence/Filter/Content/Handler.php b/src/contracts/Persistence/Filter/Content/Handler.php index 9038fcfa3e..ee191ed203 100644 --- a/src/contracts/Persistence/Filter/Content/Handler.php +++ b/src/contracts/Persistence/Filter/Content/Handler.php @@ -22,7 +22,7 @@ interface Handler */ public function find(Filter $filter): iterable; - public function count(Filter $filter): int; + public function count(Filter $filter, ?int $limit = null): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Content\Handler'); diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php index 90e947cdc4..7dc811c1a6 100644 --- a/src/contracts/Persistence/Filter/Location/Handler.php +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -22,7 +22,7 @@ interface Handler */ public function find(Filter $filter): iterable; - public function count(Filter $filter): int; + public function count(Filter $filter, ?int $limit = null): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Location\Handler'); diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index 481bdc9c8e..f3c01f7694 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -544,8 +544,10 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * @param array $languages A list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. + * @param int|null $limit If set, the count will be limited to first $limit items found. + * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null): int; + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int; } class_alias(ContentService::class, 'eZ\Publish\API\Repository\ContentService'); diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index 3575c0df42..fa6d170c84 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -286,9 +286,9 @@ public function find(Filter $filter, ?array $languages = null): ContentList return $this->innerService->find($filter, $languages); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { - return $this->innerService->count($filter, $languages); + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 5f11c4cab3..1aae83db24 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -82,14 +82,14 @@ public function loadParentLocationsForDraftContent( return $this->innerService->loadParentLocationsForDraftContent($versionInfo, $prioritizedLanguages); } - public function getLocationChildCount(Location $location): int + public function getLocationChildCount(Location $location, ?int $limit = null ): int { - return $this->innerService->getLocationChildCount($location); + return $this->innerService->getLocationChildCount($location, $limit); } - public function getSubtreeSize(Location $location): int + public function getSubtreeSize(Location $location, ?int $limit = null): int { - return $this->innerService->getSubtreeSize($location); + return $this->innerService->getSubtreeSize($location, $limit); } public function createLocation( @@ -160,9 +160,9 @@ public function find(Filter $filter, ?array $languages = null): LocationList return $this->innerService->find($filter, $languages); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { - return $this->innerService->count($filter, $languages); + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index a86c990d5b..700d74eeed 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -124,14 +124,14 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar * * @return int */ - public function getLocationChildCount(Location $location): int; + public function getLocationChildCount(Location $location, ?int $limit = null): int; /** * Return the subtree size of a given location. * * Warning! This method is not permission aware by design. */ - public function getSubtreeSize(Location $location): int; + public function getSubtreeSize(Location $location, ?int $limit = null): int; /** * Creates the new $location in the content repository for the given content. @@ -274,8 +274,10 @@ public function find(Filter $filter, ?array $languages = null): LocationList; * @param array|null $languages a list of language codes to be added as additional constraints. * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set * for a SiteAccess in a current context will be used. + * @param int|null $limit If set, the count will be limited to first $limit items found. + * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null): int; + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int; } class_alias(LocationService::class, 'eZ\Publish\API\Repository\LocationService'); diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index 74a50a2039..d0d5cda196 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -256,13 +256,13 @@ public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) return $this->persistenceHandler->locationHandler()->copySubtree($sourceId, $destinationParentId, $newOwnerId); } - public function getSubtreeSize(string $path): int + public function getSubtreeSize(string $path, ?int $limit = null): int { $this->logger->logCall(__METHOD__, [ 'path' => $path, ]); - return $this->persistenceHandler->locationHandler()->getSubtreeSize($path); + return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); } /** diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway.php b/src/lib/Persistence/Legacy/Content/Location/Gateway.php index 5218cad951..af6cf17990 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway.php @@ -118,7 +118,7 @@ abstract public function getSubtreeContent(int $sourceId, bool $onlyIds = false) */ abstract public function getSubtreeChildrenDraftContentIds(int $sourceId): array; - abstract public function getSubtreeSize(string $path): int; + abstract public function getSubtreeSize(string $path, ?int $limit = null): int; /** * Returns data for the first level children of the location identified by given $locationId. diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index bfd06ecb2a..97b0a066e0 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -20,6 +20,7 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway; +use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; use PDO; @@ -35,6 +36,8 @@ */ final class DoctrineDatabase extends Gateway { + use LimitedCountQueryTrait; + /** @var \Doctrine\DBAL\Connection */ private $connection; @@ -260,7 +263,7 @@ public function getSubtreeChildrenDraftContentIds(int $sourceId): array return $statement->fetchFirstColumn(); } - public function getSubtreeSize(string $path): int + public function getSubtreeSize(string $path, ?int $limit = null): int { $query = $this->createNodeQueryBuilder([$this->dbPlatform->getCountExpression('node_id')]); $query->andWhere( @@ -272,6 +275,12 @@ public function getSubtreeSize(string $path): int ) ); + $query = $this->wrapCountQuery( + $query, + 't.node_id', + $limit + ); + return (int) $query->execute()->fetchOne(); } diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php index e2d3c5db23..011cee9d52 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php @@ -118,10 +118,10 @@ public function getSubtreeChildrenDraftContentIds(int $sourceId): array } } - public function getSubtreeSize(string $path): int + public function getSubtreeSize(string $path, ?int $limit = null): int { try { - return $this->innerGateway->getSubtreeSize($path); + return $this->innerGateway->getSubtreeSize($path, $limit); } catch (DBALException | PDOException $e) { throw DatabaseException::wrap($e); } diff --git a/src/lib/Persistence/Legacy/Content/Location/Handler.php b/src/lib/Persistence/Legacy/Content/Location/Handler.php index 8ce6a52493..a0926a2459 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Handler.php +++ b/src/lib/Persistence/Legacy/Content/Location/Handler.php @@ -332,9 +332,9 @@ public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) return $copiedSubtreeRootLocation; } - public function getSubtreeSize(string $path): int + public function getSubtreeSize(string $path, ?int $limit = null): int { - return $this->locationGateway->getSubtreeSize($path); + return $this->locationGateway->getSubtreeSize($path, $limit); } /** diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index 18a2424afc..0fbdd8d33e 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -8,6 +8,7 @@ namespace Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine; +use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; use function array_filter; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; @@ -31,6 +32,8 @@ */ final class DoctrineGateway implements Gateway { + use LimitedCountQueryTrait; + public const COLUMN_MAP = [ // Content Info 'content_id' => 'content.id', @@ -87,13 +90,19 @@ private function getDatabasePlatform(): AbstractPlatform } } - public function count(FilteringCriterion $criterion): int + public function count(FilteringCriterion $criterion, ?int $limit = null): int { $query = $this->buildQuery( [$this->getDatabasePlatform()->getCountExpression('DISTINCT content.id')], $criterion ); + $query = $this->wrapCountQuery( + $query, + 'content.id', + $limit + ); + return (int)$query->execute()->fetch(FetchMode::COLUMN); } diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Gateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Gateway.php index 993b015f8a..415aa91651 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Gateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Gateway.php @@ -18,9 +18,9 @@ interface Gateway { /** - * Return number of matched rows for the given Criteria (a total count w/o pagination constraints). + * Return number of matched rows for the given Criteria (a total count w/o pagination constraints, Unless a limit is passed). */ - public function count(FilteringCriterion $criterion): int; + public function count(FilteringCriterion $criterion, ?int $limit = null): int; /** * Return iterator for raw Repository data for the given Query result filtered by the given Criteria, diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php index 6f0b9d74d6..9c863adc78 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php @@ -20,12 +20,15 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; use Ibexa\Core\Persistence\Legacy\Filter\Gateway\Gateway; +use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; /** * @internal for internal use by Legacy Storage */ final class DoctrineGateway implements Gateway { + use LimitedCountQueryTrait; + /** @var \Doctrine\DBAL\Connection */ private $connection; @@ -54,12 +57,18 @@ private function getDatabasePlatform(): AbstractPlatform } } - public function count(FilteringCriterion $criterion): int + public function count(FilteringCriterion $criterion, ?int $limit = null): int { $query = $this->buildQuery($criterion); $query->select($this->getDatabasePlatform()->getCountExpression('DISTINCT location.node_id')); + $query = $this->wrapCountQuery( + $query, + 'location.node_id', + $limit + ); + return (int)$query->execute()->fetch(FetchMode::COLUMN); } diff --git a/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php b/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php index 7676b18a1c..4d81ccf922 100644 --- a/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php +++ b/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php @@ -73,9 +73,9 @@ function (array $row): ContentItem { return $list; } - public function count(Filter $filter): int + public function count(Filter $filter, ?int $limit = null): int { - return $this->gateway->count($filter->getCriterion()); + return $this->gateway->count($filter->getCriterion(), $limit); } } diff --git a/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php b/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php index a5bdacb382..a99c2e5f97 100644 --- a/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php +++ b/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php @@ -69,9 +69,9 @@ function (array $row): LocationWithContentInfo { return $list; } - public function count(Filter $filter): int + public function count(Filter $filter, ?int $limit = null): int { - return $this->gateway->count($filter->getCriterion()); + return $this->gateway->count($filter->getCriterion(), $limit); } } diff --git a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php new file mode 100644 index 0000000000..7ba8492836 --- /dev/null +++ b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php @@ -0,0 +1,56 @@ + 0; + + if (!$useLimit) { + return $queryBuilder; + } + + $querySql = $queryBuilder->select($countableField) + ->setMaxResults($limit) + ->getSQL(); + + $countQuery = $this->connection->createQueryBuilder(); + + return $countQuery + ->select( + $queryBuilder->getConnection()->getDatabasePlatform()->getCountExpression('*') + ) + ->from('(' . $querySql . ')', 'csub') + ->setParameters($queryBuilder->getParameters(), $queryBuilder->getParameterTypes()); + } +} \ No newline at end of file diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 16cd434e61..777b755ff9 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2713,7 +2713,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList return new ContentList($contentItemsIterator->getTotalCount(), $contentItems); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { $filter = clone $filter; if (!empty($languages)) { @@ -2733,7 +2733,7 @@ public function count(Filter $filter, ?array $languages = null): int $filter->andWithCriterion($permissionCriterion); } - return $this->contentFilteringHandler->count($filter); + return $this->contentFilteringHandler->count($filter, $limit); } } diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 6c71114051..e59c6419d9 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -372,11 +372,11 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar /** * Returns the number of children which are readable by the current user of a Location object. */ - public function getLocationChildCount(APILocation $location): int + public function getLocationChildCount(Location $location, ?int $limit = null): int { $filter = $this->buildLocationChildrenFilter($location); - return $this->count($filter); + return $this->count($filter, null, $limit); } public function getSubtreeSize(APILocation $location): int @@ -942,7 +942,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList ); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { $filter = clone $filter; if (!empty($languages)) { @@ -962,7 +962,7 @@ public function count(Filter $filter, ?array $languages = null): int $filter->andWithCriterion($permissionCriterion); } - return $this->locationFilteringHandler->count($filter); + return $this->locationFilteringHandler->count($filter, $limit); } /** diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index f931f59b1f..a9ff350a02 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -297,11 +297,12 @@ public function find(Filter $filter, ?array $languages = null): ContentList ); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { return $this->service->count( $filter, - $this->languageResolver->getPrioritizedLanguages($languages) + $this->languageResolver->getPrioritizedLanguages($languages), + $limit ); } } diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index 43e6feb7c9..cfaa3c5320 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -104,14 +104,14 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar ); } - public function getLocationChildCount(Location $location): int + public function getLocationChildCount(Location $location, ?int $limit = null ): int { - return $this->service->getLocationChildCount($location); + return $this->service->getLocationChildCount($location, $limit); } - public function getSubtreeSize(Location $location): int + public function getSubtreeSize(Location $location, ?int $limit = null): int { - return $this->service->getSubtreeSize($location); + return $this->service->getSubtreeSize($location, $limit); } public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location @@ -192,11 +192,12 @@ public function find(Filter $filter, ?array $languages = null): LocationList ); } - public function count(Filter $filter, ?array $languages = null): int + public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int { return $this->service->count( $filter, - $this->languageResolver->getPrioritizedLanguages($languages) + $this->languageResolver->getPrioritizedLanguages($languages), + $limit ); } } diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 350347bda4..1e6f443da4 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1114,6 +1114,26 @@ public function testGetLocationChildCount() ); } + /** + * Test for the getLocationChildCount() method with a limitation on the number of children. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::getLocationChildCount() + * @depends testLoadLocation + */ + public function testGetLocationChildCountWithLimitation() + { + // $locationId is the ID of an existing location + $locationService = $this->getRepository()->getLocationService(); + + $this->assertSame( + 2, + $locationService->getLocationChildCount( + $locationService->loadLocation($this->generateId('location', 5)), + 2 + ) + ); + } + /** * Test for the loadLocationChildren() method. * @@ -3556,6 +3576,42 @@ public function testGetSubtreeSize(): Location return $location; } + public function testGetSubtreeSizeWithLimit(): Location + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); + $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertSame(1, $locationService->getSubtreeSize($location)); + + for ($i = 1; $i <= 10; ++$i) { + $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); + } + + self::assertSame(3, $locationService->getSubtreeSize($location, 3)); + + return $location; + } + + public function testGetSubtreeSizeWithInvalidLimitHasNoEffect(): Location + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); + $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertSame(1, $locationService->getSubtreeSize($location)); + + for ($i = 1; $i <= 10; ++$i) { + $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); + } + + self::assertSame(11, $locationService->getSubtreeSize($location, -2)); + + return $location; + } + /** * Loads properties from all locations in the $location's subtree. * diff --git a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php index 2e579230e3..b11940719f 100644 --- a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php @@ -151,7 +151,7 @@ public function testGetLocationChildCountDecorator() $serviceMock = $this->createServiceMock(); $decoratedService = $this->createDecorator($serviceMock); - $parameters = [$this->createMock(Location::class)]; + $parameters = [$this->createMock(Location::class), 8]; $serviceMock->expects($this->once())->method('getLocationChildCount')->with(...$parameters); From 2283e738614b85f9d5b79d62645930cdea0897ee Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 20 Jun 2025 00:48:33 +0100 Subject: [PATCH 02/23] IBX-10186 Fix location service --- src/lib/Repository/LocationService.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index e59c6419d9..260a4eabb3 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -379,10 +379,11 @@ public function getLocationChildCount(Location $location, ?int $limit = null): i return $this->count($filter, null, $limit); } - public function getSubtreeSize(APILocation $location): int + public function getSubtreeSize(APILocation $location, ?int $limit = null): int { return $this->persistenceHandler->locationHandler()->getSubtreeSize( - $location->getPathString() + $location->getPathString(), + $limit ); } From e0112db5161374f2fe78f47cfc1a2966f4a3edd2 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 20 Jun 2025 13:09:12 +0100 Subject: [PATCH 03/23] IBX-10186 CS Fixes --- src/contracts/Repository/Decorator/LocationServiceDecorator.php | 2 +- .../Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php | 2 +- .../Legacy/Traits/Doctrine/LimitedCountQueryTrait.php | 2 +- src/lib/Repository/SiteAccessAware/LocationService.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 1aae83db24..3cb27a11f0 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -82,7 +82,7 @@ public function loadParentLocationsForDraftContent( return $this->innerService->loadParentLocationsForDraftContent($versionInfo, $prioritizedLanguages); } - public function getLocationChildCount(Location $location, ?int $limit = null ): int + public function getLocationChildCount(Location $location, ?int $limit = null): int { return $this->innerService->getLocationChildCount($location, $limit); } diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index 0fbdd8d33e..b21220fcae 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -8,7 +8,6 @@ namespace Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine; -use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; use function array_filter; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; @@ -23,6 +22,7 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; use Ibexa\Core\Persistence\Legacy\Filter\Gateway\Gateway; +use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; use function iterator_to_array; use function sprintf; use Traversable; diff --git a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php index 7ba8492836..58d27d6ddc 100644 --- a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php +++ b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php @@ -53,4 +53,4 @@ protected function wrapCountQuery( ->from('(' . $querySql . ')', 'csub') ->setParameters($queryBuilder->getParameters(), $queryBuilder->getParameterTypes()); } -} \ No newline at end of file +} diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index cfaa3c5320..cccf313a08 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -104,7 +104,7 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar ); } - public function getLocationChildCount(Location $location, ?int $limit = null ): int + public function getLocationChildCount(Location $location, ?int $limit = null): int { return $this->service->getLocationChildCount($location, $limit); } From 88043e877d26295a8f41c0e7fc9e1e681ecd6996 Mon Sep 17 00:00:00 2001 From: ryanolee Date: Fri, 20 Jun 2025 14:47:34 +0100 Subject: [PATCH 04/23] Update tests/integration/Core/Repository/LocationServiceTest.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adam Wójs --- tests/integration/Core/Repository/LocationServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 1e6f443da4..8fef869c26 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1120,7 +1120,7 @@ public function testGetLocationChildCount() * @covers \Ibexa\Contracts\Core\Repository\LocationService::getLocationChildCount() * @depends testLoadLocation */ - public function testGetLocationChildCountWithLimitation() + public function testGetLocationChildCountWithLimitation(): void { // $locationId is the ID of an existing location $locationService = $this->getRepository()->getLocationService(); From d886f29a885f79242175b9957c30917c6e597928 Mon Sep 17 00:00:00 2001 From: ryanolee Date: Fri, 20 Jun 2025 14:47:41 +0100 Subject: [PATCH 05/23] Update src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adam Wójs --- .../Legacy/Traits/Doctrine/LimitedCountQueryTrait.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php index 58d27d6ddc..9955ae71f0 100644 --- a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php +++ b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php @@ -22,12 +22,6 @@ trait LimitedCountQueryTrait * SELECT DISTINCT COUNT(DISTINCT someField) FROM XXX WHERE YYY; * To * SELECT COUNT(*) FROM (SELECT DISTINCT someField FROM XXX WHERE YYY LIMIT N) AS csub;. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param string $countableField - * @param mixed $limit - * - * @return \Doctrine\DBAL\Query\QueryBuilder */ protected function wrapCountQuery( QueryBuilder $queryBuilder, From 3beb6745ecb6e8c461d01bd9d9441c4a77f04c54 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 21 Jun 2025 01:04:19 +0100 Subject: [PATCH 06/23] IBX-10186 Use symfony BC break approach --- .../Persistence/Content/Location/Handler.php | 5 +- .../Persistence/Filter/Content/Handler.php | 6 +- .../Persistence/Filter/Location/Handler.php | 6 +- src/contracts/Repository/ContentService.php | 2 +- .../Decorator/ContentServiceDecorator.php | 6 +- .../Decorator/LocationServiceDecorator.php | 18 +++- src/contracts/Repository/LocationService.php | 9 +- src/lib/Persistence/Cache/LocationHandler.php | 9 +- .../Location/Gateway/DoctrineDatabase.php | 13 +-- .../Content/Doctrine/DoctrineGateway.php | 13 +-- .../Location/Doctrine/DoctrineGateway.php | 15 ++-- .../Filter/Query/LimitedCountQueryBuilder.php | 73 ++++++++++++++++ .../Doctrine/LimitedCountQueryTrait.php | 50 ----------- src/lib/Repository/ContentService.php | 8 +- src/lib/Repository/LocationService.php | 16 +++- .../SiteAccessAware/ContentService.php | 6 +- .../SiteAccessAware/LocationService.php | 15 +++- .../Query/LimitedCountQueryBuilderTest.php | 84 +++++++++++++++++++ 18 files changed, 266 insertions(+), 88 deletions(-) create mode 100644 src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php delete mode 100644 src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php create mode 100644 tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index b548a38daf..994f4d8d9c 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -110,7 +110,10 @@ public function loadParentLocationsForDraftContent($contentId); */ public function copySubtree($sourceId, $destinationParentId); - public function getSubtreeSize(string $path, ?int $limit = null): int; + /** + * @param int|null $limit + */ + public function getSubtreeSize(string $path, /* ?int $limit = null */): int; /** * Moves location identified by $sourceId into new parent identified by $destinationParentId. diff --git a/src/contracts/Persistence/Filter/Content/Handler.php b/src/contracts/Persistence/Filter/Content/Handler.php index ee191ed203..7171c12f19 100644 --- a/src/contracts/Persistence/Filter/Content/Handler.php +++ b/src/contracts/Persistence/Filter/Content/Handler.php @@ -22,7 +22,11 @@ interface Handler */ public function find(Filter $filter): iterable; - public function count(Filter $filter, ?int $limit = null): int; + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter + * @param int|null $limit + */ + public function count(Filter $filter, /* ?int $limit = null */): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Content\Handler'); diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php index 7dc811c1a6..ce662914c2 100644 --- a/src/contracts/Persistence/Filter/Location/Handler.php +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -22,7 +22,11 @@ interface Handler */ public function find(Filter $filter): iterable; - public function count(Filter $filter, ?int $limit = null): int; + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter + * @param int|null $limit + */ + public function count(Filter $filter, /* ?int $limit = null */): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Location\Handler'); diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index f3c01f7694..fb0775b59d 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -547,7 +547,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int; + public function count(Filter $filter, ?array $languages = null, /* ?int $limit = null */): int; } class_alias(ContentService::class, 'eZ\Publish\API\Repository\ContentService'); diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index fa6d170c84..8b0859d648 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -286,8 +286,12 @@ public function find(Filter $filter, ?array $languages = null): ContentList return $this->innerService->find($filter, $languages); } - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 3cb27a11f0..5afe118d39 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -82,13 +82,21 @@ public function loadParentLocationsForDraftContent( return $this->innerService->loadParentLocationsForDraftContent($versionInfo, $prioritizedLanguages); } - public function getLocationChildCount(Location $location, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function getLocationChildCount(Location $location /* ?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; return $this->innerService->getLocationChildCount($location, $limit); } - public function getSubtreeSize(Location $location, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function getSubtreeSize(Location $location /* ?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; return $this->innerService->getSubtreeSize($location, $limit); } @@ -160,8 +168,12 @@ public function find(Filter $filter, ?array $languages = null): LocationList return $this->innerService->find($filter, $languages); } - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index 700d74eeed..207c85d647 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -121,17 +121,20 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar * Returns the number of children which are readable by the current user of a location object. * * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param int|null $limit If set, the count will be limited to first $limit items found. * * @return int */ - public function getLocationChildCount(Location $location, ?int $limit = null): int; + public function getLocationChildCount(Location $location /* ?int $limit = null */): int; /** * Return the subtree size of a given location. * * Warning! This method is not permission aware by design. + * + * @param int|null $limit */ - public function getSubtreeSize(Location $location, ?int $limit = null): int; + public function getSubtreeSize(Location $location /* ?int $limit = null */): int; /** * Creates the new $location in the content repository for the given content. @@ -277,7 +280,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int; + public function count(Filter $filter, ?array $languages = null, /* ?int $limit = null */): int; } class_alias(LocationService::class, 'eZ\Publish\API\Repository\LocationService'); diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index d0d5cda196..2d2932a1f3 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -256,10 +256,15 @@ public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) return $this->persistenceHandler->locationHandler()->copySubtree($sourceId, $destinationParentId, $newOwnerId); } - public function getSubtreeSize(string $path, ?int $limit = null): int - { + /** + * {@inheritdoc} + */ + public function getSubtreeSize(string $path, /* ?int $limit = null */): int + { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; $this->logger->logCall(__METHOD__, [ 'path' => $path, + 'limit' => $limit, ]); return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index 97b0a066e0..af58db3dc0 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -20,7 +20,7 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway; -use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; +use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; use PDO; @@ -36,8 +36,6 @@ */ final class DoctrineDatabase extends Gateway { - use LimitedCountQueryTrait; - /** @var \Doctrine\DBAL\Connection */ private $connection; @@ -53,6 +51,9 @@ final class DoctrineDatabase extends Gateway /** @var \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter */ private $trashSortClauseConverter; + /** @var Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ + private $limitedCountQueryBuilder; + /** * @throws \Doctrine\DBAL\DBALException */ @@ -60,13 +61,15 @@ public function __construct( Connection $connection, MaskGenerator $languageMaskGenerator, CriteriaConverter $trashCriteriaConverter, - SortClauseConverter $trashSortClauseConverter + SortClauseConverter $trashSortClauseConverter, + LimitedCountQueryBuilder $limitedCountQueryBuilder, ) { $this->connection = $connection; $this->dbPlatform = $this->connection->getDatabasePlatform(); $this->languageMaskGenerator = $languageMaskGenerator; $this->trashCriteriaConverter = $trashCriteriaConverter; $this->trashSortClauseConverter = $trashSortClauseConverter; + $this->limitedCountQueryBuilder = $limitedCountQueryBuilder; } public function getBasicNodeData( @@ -275,7 +278,7 @@ public function getSubtreeSize(string $path, ?int $limit = null): int ) ); - $query = $this->wrapCountQuery( + $query = $this->limitedCountQueryBuilder->wrap( $query, 't.node_id', $limit diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index b21220fcae..bc4a94e1e8 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -8,6 +8,7 @@ namespace Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine; +use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use function array_filter; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; @@ -22,7 +23,6 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; use Ibexa\Core\Persistence\Legacy\Filter\Gateway\Gateway; -use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; use function iterator_to_array; use function sprintf; use Traversable; @@ -32,8 +32,6 @@ */ final class DoctrineGateway implements Gateway { - use LimitedCountQueryTrait; - public const COLUMN_MAP = [ // Content Info 'content_id' => 'content.id', @@ -71,14 +69,19 @@ final class DoctrineGateway implements Gateway /** @var \Ibexa\Contracts\Core\Persistence\Filter\SortClauseVisitor */ private $sortClauseVisitor; + /** @var Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ + private $limitedCountQueryBuilder; + public function __construct( Connection $connection, CriterionVisitor $criterionVisitor, - SortClauseVisitor $sortClauseVisitor + SortClauseVisitor $sortClauseVisitor, + LimitedCountQueryBuilder $limitedCountQueryBuilder ) { $this->connection = $connection; $this->criterionVisitor = $criterionVisitor; $this->sortClauseVisitor = $sortClauseVisitor; + $this->limitedCountQueryBuilder = $limitedCountQueryBuilder; } private function getDatabasePlatform(): AbstractPlatform @@ -97,7 +100,7 @@ public function count(FilteringCriterion $criterion, ?int $limit = null): int $criterion ); - $query = $this->wrapCountQuery( + $query = $this->limitedCountQueryBuilder->wrap( $query, 'content.id', $limit diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php index 9c863adc78..afc10301ed 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php @@ -20,15 +20,13 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; use Ibexa\Core\Persistence\Legacy\Filter\Gateway\Gateway; -use Ibexa\Core\Persistence\Legacy\Traits\Doctrine\LimitedCountQueryTrait; +use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; /** * @internal for internal use by Legacy Storage */ final class DoctrineGateway implements Gateway { - use LimitedCountQueryTrait; - /** @var \Doctrine\DBAL\Connection */ private $connection; @@ -38,14 +36,19 @@ final class DoctrineGateway implements Gateway /** @var \Ibexa\Contracts\Core\Persistence\Filter\SortClauseVisitor */ private $sortClauseVisitor; + /** @var \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ + private $limitedCountQueryBuilder; + public function __construct( Connection $connection, CriterionVisitor $criterionVisitor, - SortClauseVisitor $sortClauseVisitor + SortClauseVisitor $sortClauseVisitor, + LimitedCountQueryBuilder $limitedCountQueryBuilder ) { $this->connection = $connection; $this->criterionVisitor = $criterionVisitor; $this->sortClauseVisitor = $sortClauseVisitor; + $this->limitedCountQueryBuilder = $limitedCountQueryBuilder; } private function getDatabasePlatform(): AbstractPlatform @@ -62,8 +65,8 @@ public function count(FilteringCriterion $criterion, ?int $limit = null): int $query = $this->buildQuery($criterion); $query->select($this->getDatabasePlatform()->getCountExpression('DISTINCT location.node_id')); - - $query = $this->wrapCountQuery( + + $query = $this->limitedCountQueryBuilder->wrap( $query, 'location.node_id', $limit diff --git a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php new file mode 100644 index 0000000000..b32fa647ad --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php @@ -0,0 +1,73 @@ +connection = $connection; + } + + + /** + * Takes a QueryBuilder and wraps it in a count query with a limit if a limit is provided. + * This performs the following transformation to the passed query. + * SELECT DISTINCT COUNT(DISTINCT someField) FROM XXX WHERE YYY; + * To + * SELECT COUNT(*) FROM (SELECT DISTINCT someField FROM XXX WHERE YYY LIMIT N) AS csub;. + * + * @phpstan-param positive-int $limit + * @throws InvalidArgumentException + * @throws Exception + */ + public function wrap( + QueryBuilder $queryBuilder, + string $countableField, + ?int $limit = null, + ): QueryBuilder { + if ($limit === null) { + return $queryBuilder; + } + + if($limit <= 0) { + throw new InvalidArgumentException('$limit', 'Limit must be greater than 0'); + } + + $querySql = $queryBuilder + ->select($countableField) + ->setMaxResults($limit) + ->getSQL(); + + $countQuery = $this->connection->createQueryBuilder(); + + return $countQuery + ->select( + $this->connection->getDatabasePlatform()->getCountExpression('*') + ) + ->from('(' . $querySql . ')', 'csub') + ->setParameters($queryBuilder->getParameters(), $queryBuilder->getParameterTypes()); + } +} diff --git a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php b/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php deleted file mode 100644 index 9955ae71f0..0000000000 --- a/src/lib/Persistence/Legacy/Traits/Doctrine/LimitedCountQueryTrait.php +++ /dev/null @@ -1,50 +0,0 @@ - 0; - - if (!$useLimit) { - return $queryBuilder; - } - - $querySql = $queryBuilder->select($countableField) - ->setMaxResults($limit) - ->getSQL(); - - $countQuery = $this->connection->createQueryBuilder(); - - return $countQuery - ->select( - $queryBuilder->getConnection()->getDatabasePlatform()->getCountExpression('*') - ) - ->from('(' . $querySql . ')', 'csub') - ->setParameters($queryBuilder->getParameters(), $queryBuilder->getParameterTypes()); - } -} diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 777b755ff9..c3da7cc5be 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2712,9 +2712,13 @@ public function find(Filter $filter, ?array $languages = null): ContentList return new ContentList($contentItemsIterator->getTotalCount(), $contentItems); } - - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + + /** + * @param null|int $limit + */ + public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; $filter = clone $filter; if (!empty($languages)) { $filter->andWithCriterion(new LanguageCode($languages)); diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 260a4eabb3..6b0a093f6f 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -372,15 +372,21 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar /** * Returns the number of children which are readable by the current user of a Location object. */ - public function getLocationChildCount(Location $location, ?int $limit = null): int + public function getLocationChildCount(APILocation $location /*?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; $filter = $this->buildLocationChildrenFilter($location); return $this->count($filter, null, $limit); } - public function getSubtreeSize(APILocation $location, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function getSubtreeSize(APILocation $location /* ?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; + return $this->persistenceHandler->locationHandler()->getSubtreeSize( $location->getPathString(), $limit @@ -943,8 +949,12 @@ public function find(Filter $filter, ?array $languages = null): LocationList ); } - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + /** + * @param null|int $limit + */ + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; $filter = clone $filter; if (!empty($languages)) { $filter->andWithCriterion(new LanguageCode($languages)); diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index a9ff350a02..767b98afb9 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -297,8 +297,12 @@ public function find(Filter $filter, ?array $languages = null): ContentList ); } - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + /** + * @param null|int $limit + */ + public function count(Filter $filter, ?array $languages = null, /*?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index cccf313a08..ce83b62811 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -104,13 +104,18 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar ); } - public function getLocationChildCount(Location $location, ?int $limit = null): int + public function getLocationChildCount(Location $location /* ?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; return $this->service->getLocationChildCount($location, $limit); } - public function getSubtreeSize(Location $location, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function getSubtreeSize(Location $location, /* ?int $limit = null */): int { + $limit = func_num_args() > 1 ? func_get_arg(1) : null; return $this->service->getSubtreeSize($location, $limit); } @@ -192,8 +197,12 @@ public function find(Filter $filter, ?array $languages = null): LocationList ); } - public function count(Filter $filter, ?array $languages = null, ?int $limit = null): int + /** + * @param int|null $limit + */ + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { + $limit = func_num_args() > 2 ? func_get_arg(2) : null; return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php new file mode 100644 index 0000000000..fa8789e221 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php @@ -0,0 +1,84 @@ +limitedCountQueryBuilder = new LimitedCountQueryBuilder($this->getDatabaseConnection()); + } + + /** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder::wrap + */ + public function testWrapThrowsExceptionOnZeroLimit(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/Limit must be greater than 0/'); + + $qb = $this->getDatabaseConnection()->createQueryBuilder(); + $this->limitedCountQueryBuilder->wrap($qb, 'someField', 0); + } + + /** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder::wrap + */ + public function testWrapDoesNotChangeQueryBuilderIfLimitIsNull(): void + { + $qb = $this->getDatabaseConnection()->createQueryBuilder(); + $qb->select('DISTINCT someField') + ->from('someTable') + ->where('someCondition = :condition') + ->setParameter('condition', 'value'); + + $wrappedQueryBuilder = $this->limitedCountQueryBuilder->wrap($qb, 'someField', null); + + // The original query should remain unchanged + $this->assertEquals($qb->getSQL(), $wrappedQueryBuilder->getSQL()); + } + + /** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder::wrap + */ + public function testWrapWrapsQueryBuilderCorrectly(): void + { + $qb = $this->getDatabaseConnection()->createQueryBuilder(); + $qb->select('DISTINCT someField') + ->from('someTable') + ->where('someCondition = :condition') + ->setParameter('condition', 'value'); + + $wrappedQueryBuilder = $this->limitedCountQueryBuilder->wrap($qb, 'someField', 10); + + $expectedSql = 'SELECT COUNT(*) FROM (SELECT someField FROM someTable WHERE someCondition = :condition LIMIT 10) csub'; + $this->assertEquals($expectedSql, $wrappedQueryBuilder->getSQL()); + + + + + } + + +} \ No newline at end of file From fa1ee68cbfd9eceb373352187265b7c4fcd88ece Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 21 Jun 2025 01:05:26 +0100 Subject: [PATCH 07/23] IBX10186 CS fixup --- .../Persistence/Content/Location/Handler.php | 2 +- src/contracts/Persistence/Filter/Content/Handler.php | 2 +- .../Persistence/Filter/Location/Handler.php | 2 +- src/contracts/Repository/ContentService.php | 2 +- .../Repository/Decorator/ContentServiceDecorator.php | 1 + .../Decorator/LocationServiceDecorator.php | 3 +++ src/contracts/Repository/LocationService.php | 4 ++-- src/lib/Persistence/Cache/LocationHandler.php | 4 ++-- .../Content/Location/Gateway/DoctrineDatabase.php | 2 +- .../Gateway/Content/Doctrine/DoctrineGateway.php | 4 ++-- .../Gateway/Location/Doctrine/DoctrineGateway.php | 2 +- .../Legacy/Filter/Query/LimitedCountQueryBuilder.php | 12 +++++------- src/lib/Repository/ContentService.php | 4 ++-- src/lib/Repository/LocationService.php | 2 +- .../Repository/SiteAccessAware/ContentService.php | 5 +++-- .../Repository/SiteAccessAware/LocationService.php | 5 ++++- .../Filter/Query/LimitedCountQueryBuilderTest.php | 12 +++--------- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index 994f4d8d9c..e047d8e954 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -113,7 +113,7 @@ public function copySubtree($sourceId, $destinationParentId); /** * @param int|null $limit */ - public function getSubtreeSize(string $path, /* ?int $limit = null */): int; + public function getSubtreeSize(string $path /* ?int $limit = null */): int; /** * Moves location identified by $sourceId into new parent identified by $destinationParentId. diff --git a/src/contracts/Persistence/Filter/Content/Handler.php b/src/contracts/Persistence/Filter/Content/Handler.php index 7171c12f19..1e7b097849 100644 --- a/src/contracts/Persistence/Filter/Content/Handler.php +++ b/src/contracts/Persistence/Filter/Content/Handler.php @@ -26,7 +26,7 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ - public function count(Filter $filter, /* ?int $limit = null */): int; + public function count(Filter $filter /* ?int $limit = null */): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Content\Handler'); diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php index ce662914c2..4da15ae828 100644 --- a/src/contracts/Persistence/Filter/Location/Handler.php +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -26,7 +26,7 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ - public function count(Filter $filter, /* ?int $limit = null */): int; + public function count(Filter $filter /* ?int $limit = null */): int; } class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Filter\Location\Handler'); diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index fb0775b59d..91c5f592c6 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -547,7 +547,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null, /* ?int $limit = null */): int; + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } class_alias(ContentService::class, 'eZ\Publish\API\Repository\ContentService'); diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index 8b0859d648..025f836ab2 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -292,6 +292,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 5afe118d39..1cf35d2f16 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -88,6 +88,7 @@ public function loadParentLocationsForDraftContent( public function getLocationChildCount(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; + return $this->innerService->getLocationChildCount($location, $limit); } @@ -97,6 +98,7 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; + return $this->innerService->getSubtreeSize($location, $limit); } @@ -174,6 +176,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index 207c85d647..a24401fc9a 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -131,7 +131,7 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * * Return the subtree size of a given location. * * Warning! This method is not permission aware by design. - * + * * @param int|null $limit */ public function getSubtreeSize(Location $location /* ?int $limit = null */): int; @@ -280,7 +280,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - public function count(Filter $filter, ?array $languages = null, /* ?int $limit = null */): int; + public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } class_alias(LocationService::class, 'eZ\Publish\API\Repository\LocationService'); diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index 2d2932a1f3..c0f4221337 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -259,8 +259,8 @@ public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) /** * {@inheritdoc} */ - public function getSubtreeSize(string $path, /* ?int $limit = null */): int - { + public function getSubtreeSize(string $path /* ?int $limit = null */): int + { $limit = func_num_args() > 1 ? func_get_arg(1) : null; $this->logger->logCall(__METHOD__, [ 'path' => $path, diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index af58db3dc0..4298d618c3 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -51,7 +51,7 @@ final class DoctrineDatabase extends Gateway /** @var \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter */ private $trashSortClauseConverter; - /** @var Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ + /** @var \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ private $limitedCountQueryBuilder; /** diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index bc4a94e1e8..51f923464a 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -8,7 +8,6 @@ namespace Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine; -use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use function array_filter; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; @@ -23,6 +22,7 @@ use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; use Ibexa\Core\Persistence\Legacy\Filter\Gateway\Gateway; +use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use function iterator_to_array; use function sprintf; use Traversable; @@ -69,7 +69,7 @@ final class DoctrineGateway implements Gateway /** @var \Ibexa\Contracts\Core\Persistence\Filter\SortClauseVisitor */ private $sortClauseVisitor; - /** @var Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ + /** @var \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ private $limitedCountQueryBuilder; public function __construct( diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php index afc10301ed..54a6a19d9d 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php @@ -65,7 +65,7 @@ public function count(FilteringCriterion $criterion, ?int $limit = null): int $query = $this->buildQuery($criterion); $query->select($this->getDatabasePlatform()->getCountExpression('DISTINCT location.node_id')); - + $query = $this->limitedCountQueryBuilder->wrap( $query, 'location.node_id', diff --git a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php index b32fa647ad..e1345c8085 100644 --- a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php +++ b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php @@ -8,9 +8,7 @@ namespace Ibexa\Core\Persistence\Legacy\Filter\Query; - use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Exception; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; @@ -23,7 +21,7 @@ final class LimitedCountQueryBuilder /** * @var \Doctrine\DBAL\Connection */ - private $connection; + private $connection; public function __construct( Connection $connection, @@ -31,7 +29,6 @@ public function __construct( $this->connection = $connection; } - /** * Takes a QueryBuilder and wraps it in a count query with a limit if a limit is provided. * This performs the following transformation to the passed query. @@ -40,8 +37,9 @@ public function __construct( * SELECT COUNT(*) FROM (SELECT DISTINCT someField FROM XXX WHERE YYY LIMIT N) AS csub;. * * @phpstan-param positive-int $limit - * @throws InvalidArgumentException - * @throws Exception + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Doctrine\DBAL\Exception */ public function wrap( QueryBuilder $queryBuilder, @@ -52,7 +50,7 @@ public function wrap( return $queryBuilder; } - if($limit <= 0) { + if ($limit <= 0) { throw new InvalidArgumentException('$limit', 'Limit must be greater than 0'); } diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index c3da7cc5be..cc26ab1a71 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2712,9 +2712,9 @@ public function find(Filter $filter, ?array $languages = null): ContentList return new ContentList($contentItemsIterator->getTotalCount(), $contentItems); } - + /** - * @param null|int $limit + * @param int|null $limit */ public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 6b0a093f6f..37dfe82afe 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -950,7 +950,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList } /** - * @param null|int $limit + * @param int|null $limit */ public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index 767b98afb9..b2f024fd6e 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -298,11 +298,12 @@ public function find(Filter $filter, ?array $languages = null): ContentList } /** - * @param null|int $limit + * @param int|null $limit */ - public function count(Filter $filter, ?array $languages = null, /*?int $limit = null */): int + public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index ce83b62811..3f57f5daf3 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -107,15 +107,17 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar public function getLocationChildCount(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; + return $this->service->getLocationChildCount($location, $limit); } /** * @param int|null $limit */ - public function getSubtreeSize(Location $location, /* ?int $limit = null */): int + public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; + return $this->service->getSubtreeSize($location, $limit); } @@ -203,6 +205,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php index fa8789e221..81ab63cee0 100644 --- a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php @@ -10,7 +10,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Query\QueryBuilder; use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; @@ -18,11 +17,12 @@ /** * @covers \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ - class LimitedCountQueryBuilderTest extends TestCase { private Connection $connectionMock; + private AbstractPlatform $platformMock; + private LimitedCountQueryBuilder $limitedCountQueryBuilder; protected function setUp(): void @@ -74,11 +74,5 @@ public function testWrapWrapsQueryBuilderCorrectly(): void $expectedSql = 'SELECT COUNT(*) FROM (SELECT someField FROM someTable WHERE someCondition = :condition LIMIT 10) csub'; $this->assertEquals($expectedSql, $wrappedQueryBuilder->getSQL()); - - - - } - - -} \ No newline at end of file +} From 5b2fe00d7a383e020147aa1ddc411db511ac387a Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 21 Jun 2025 01:21:18 +0100 Subject: [PATCH 08/23] IBX-10186 --- .../Resources/settings/storage_engines/legacy/filter.yaml | 4 ++++ .../Resources/settings/storage_engines/legacy/location.yml | 1 + .../Content/Location/Gateway/DoctrineDatabaseTest.php | 3 ++- .../Content/Location/Gateway/DoctrineDatabaseTrashTest.php | 3 ++- .../Legacy/Content/UrlAlias/UrlAliasHandlerTest.php | 3 ++- tests/lib/Persistence/Legacy/TestCase.php | 6 ++++++ 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/lib/Resources/settings/storage_engines/legacy/filter.yaml b/src/lib/Resources/settings/storage_engines/legacy/filter.yaml index 4ac9426423..54022002cb 100644 --- a/src/lib/Resources/settings/storage_engines/legacy/filter.yaml +++ b/src/lib/Resources/settings/storage_engines/legacy/filter.yaml @@ -56,3 +56,7 @@ services: arguments: $gateway: '@Ibexa\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway' $locationMapper: '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' + + Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder: + arguments: + $connection: '@ibexa.persistence.connection' \ No newline at end of file diff --git a/src/lib/Resources/settings/storage_engines/legacy/location.yml b/src/lib/Resources/settings/storage_engines/legacy/location.yml index 3b0ba0cde5..ee3a7a813a 100644 --- a/src/lib/Resources/settings/storage_engines/legacy/location.yml +++ b/src/lib/Resources/settings/storage_engines/legacy/location.yml @@ -6,6 +6,7 @@ services: - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' - '@ibexa.core.trash.search.legacy.gateway.criteria_converter' - '@ibexa.core.trash.search.legacy.gateway.sort_clause_converter' + - '@Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder' Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion: class: Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php index 789530d83d..62c6a10100 100644 --- a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php @@ -28,7 +28,8 @@ protected function getLocationGateway() $this->getDatabaseConnection(), $this->getLanguageMaskGenerator(), $this->getTrashCriteriaConverterDependency(), - $this->getTrashSortClauseConverterDependency() + $this->getTrashSortClauseConverterDependency(), + $this->getLimitedCountQueryBuilderDependency() ); } diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php index 322dfacd79..bfd587ed87 100644 --- a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php @@ -24,7 +24,8 @@ protected function getLocationGateway() $this->getDatabaseConnection(), $this->getLanguageMaskGenerator(), $this->getTrashCriteriaConverterDependency(), - $this->getTrashSortClauseConverterDependency() + $this->getTrashSortClauseConverterDependency(), + $this->getLimitedCountQueryBuilderDependency(), ); } diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php index 13569aefd1..0c1df662f6 100644 --- a/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php @@ -5431,7 +5431,8 @@ protected function getLocationGateway() $this->getDatabaseConnection(), $this->getLanguageMaskGenerator(), $this->getTrashCriteriaConverterDependency(), - $this->getTrashSortClauseConverterDependency() + $this->getTrashSortClauseConverterDependency(), + $this->getLimitedCountQueryBuilderDependency() ); } diff --git a/tests/lib/Persistence/Legacy/TestCase.php b/tests/lib/Persistence/Legacy/TestCase.php index 2edd0079da..70c7ff6e16 100644 --- a/tests/lib/Persistence/Legacy/TestCase.php +++ b/tests/lib/Persistence/Legacy/TestCase.php @@ -16,6 +16,7 @@ use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use Ibexa\Core\Persistence\Legacy\SharedGateway; use Ibexa\Core\Search\Legacy\Content; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; @@ -346,6 +347,11 @@ protected function getTrashSortClauseConverterDependency(): SortClauseConverter ] ); } + + protected function getLimitedCountQueryBuilderDependency(): LimitedCountQueryBuilder + { + return new LimitedCountQueryBuilder($this->getDatabaseConnection()); + } } class_alias(TestCase::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\TestCase'); From 804f1a7fc6964a64d2745abf2c588922b27ddcb0 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 21 Jun 2025 02:24:40 +0100 Subject: [PATCH 09/23] IBX-10186 Hack to get Contract BC breaks fixed --- src/lib/Resources/settings/repository/inner.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/Resources/settings/repository/inner.yml b/src/lib/Resources/settings/repository/inner.yml index 8b5e99afcf..6d07d2e2f5 100644 --- a/src/lib/Resources/settings/repository/inner.yml +++ b/src/lib/Resources/settings/repository/inner.yml @@ -101,7 +101,10 @@ services: ibexa.api.service.inner_location: class: Ibexa\Core\Repository\LocationService factory: ['@Ibexa\Core\Repository\Repository', getLocationService] - lazy: true + # @todo - Make this lazy in V5 + # Service cannot be lazy while extra parameters reliant on func_get_args() + # are required by service implementation. + #lazy: true Ibexa\Core\Repository\LanguageService: class: Ibexa\Core\Repository\LanguageService From 12fcae7d90c185585b9a3acd81e6f24a33e80478 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 21 Jun 2025 02:34:54 +0100 Subject: [PATCH 10/23] IBX-10186 Add assert not null --- tests/integration/Core/Repository/LocationServiceTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 8fef869c26..b95edc0d81 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1124,11 +1124,12 @@ public function testGetLocationChildCountWithLimitation(): void { // $locationId is the ID of an existing location $locationService = $this->getRepository()->getLocationService(); - + $location = $locationService->loadLocation($this->generateId('location', 5)); + $this->assertNotNull($location); $this->assertSame( 2, $locationService->getLocationChildCount( - $locationService->loadLocation($this->generateId('location', 5)), + $location, 2 ) ); From 27204251c119e92d7310dfe9875f7144d800e11c Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sun, 22 Jun 2025 14:41:53 +0100 Subject: [PATCH 11/23] IBX-10186 Pre-emptiveley remove call to deprecated call --- .../Legacy/Filter/Query/LimitedCountQueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php index e1345c8085..6e0c9ed34c 100644 --- a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php +++ b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php @@ -63,7 +63,7 @@ public function wrap( return $countQuery ->select( - $this->connection->getDatabasePlatform()->getCountExpression('*') + 'COUNT(*)' ) ->from('(' . $querySql . ')', 'csub') ->setParameters($queryBuilder->getParameters(), $queryBuilder->getParameterTypes()); From 367483706e5b72366eebefb728c18c3e5035e6dc Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sun, 22 Jun 2025 18:51:24 +0100 Subject: [PATCH 12/23] IBX-10186 Add prompts for lazy loaded value holders --- .../Repository/FutureContentService.php | 23 +++++++++++++ .../Repository/FutureLocationService.php | 34 +++++++++++++++++++ .../Resources/settings/repository/inner.yml | 9 ++--- 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/contracts/Persistence/Content/Future/Repository/FutureContentService.php create mode 100644 src/contracts/Persistence/Content/Future/Repository/FutureLocationService.php diff --git a/src/contracts/Persistence/Content/Future/Repository/FutureContentService.php b/src/contracts/Persistence/Content/Future/Repository/FutureContentService.php new file mode 100644 index 0000000000..20e2a14707 --- /dev/null +++ b/src/contracts/Persistence/Content/Future/Repository/FutureContentService.php @@ -0,0 +1,23 @@ + Date: Sun, 22 Jun 2025 20:17:38 +0100 Subject: [PATCH 13/23] IBX-10186 Backwards compat and test fixes. --- .../Legacy/Filter/Query/LimitedCountQueryBuilder.php | 2 +- tests/integration/Core/Repository/LocationServiceTest.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php index 6e0c9ed34c..5d8e8808bc 100644 --- a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php +++ b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php @@ -24,7 +24,7 @@ final class LimitedCountQueryBuilder private $connection; public function __construct( - Connection $connection, + Connection $connection ) { $this->connection = $connection; } diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index b95edc0d81..839ff2b307 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -3595,7 +3595,7 @@ public function testGetSubtreeSizeWithLimit(): Location return $location; } - public function testGetSubtreeSizeWithInvalidLimitHasNoEffect(): Location + public function testGetSubtreeSizeWithInvalidLimitThrowsExpectedError(): Location { $repository = $this->getRepository(); $locationService = $repository->getLocationService(); @@ -3608,7 +3608,8 @@ public function testGetSubtreeSizeWithInvalidLimitHasNoEffect(): Location $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); } - self::assertSame(11, $locationService->getSubtreeSize($location, -2)); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageRegExp('/Limit must be greater than 0/'); return $location; } From 1f33f28620d1ccdb78f0e2d4ca5c433f8d335cc1 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sun, 22 Jun 2025 20:29:04 +0100 Subject: [PATCH 14/23] IBX-10186 Fix more php 8.1 regressions --- .../Legacy/Content/Location/Gateway/DoctrineDatabase.php | 2 +- .../Legacy/Filter/Query/LimitedCountQueryBuilder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index 4298d618c3..45edc404db 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -62,7 +62,7 @@ public function __construct( MaskGenerator $languageMaskGenerator, CriteriaConverter $trashCriteriaConverter, SortClauseConverter $trashSortClauseConverter, - LimitedCountQueryBuilder $limitedCountQueryBuilder, + LimitedCountQueryBuilder $limitedCountQueryBuilder ) { $this->connection = $connection; $this->dbPlatform = $this->connection->getDatabasePlatform(); diff --git a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php index 5d8e8808bc..28e24f164a 100644 --- a/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php +++ b/src/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilder.php @@ -44,7 +44,7 @@ public function __construct( public function wrap( QueryBuilder $queryBuilder, string $countableField, - ?int $limit = null, + ?int $limit = null ): QueryBuilder { if ($limit === null) { return $queryBuilder; From 162806fbc77024d7e0c4a1cac4ae26193d919ab0 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sun, 22 Jun 2025 21:33:37 +0100 Subject: [PATCH 15/23] IBX-10186 - PHP stan fixes --- .../Persistence/Content/Location/Handler.php | 2 ++ .../Persistence/Filter/Content/Handler.php | 1 + .../Persistence/Filter/Location/Handler.php | 1 + src/contracts/Repository/ContentService.php | 1 + .../Decorator/ContentServiceDecorator.php | 5 +++-- .../Decorator/LocationServiceDecorator.php | 9 ++++++--- src/contracts/Repository/LocationService.php | 3 +++ src/lib/Persistence/Cache/LocationHandler.php | 3 ++- .../Content/Location/Gateway/DoctrineDatabase.php | 3 +++ .../Gateway/Content/Doctrine/DoctrineGateway.php | 3 +++ .../Gateway/Location/Doctrine/DoctrineGateway.php | 3 +++ src/lib/Repository/ContentService.php | 3 ++- src/lib/Repository/LocationService.php | 11 +++++++---- .../Repository/SiteAccessAware/ContentService.php | 6 ++++-- .../Repository/SiteAccessAware/LocationService.php | 9 ++++++--- .../Core/Repository/LocationServiceTest.php | 13 ++++++++++++- .../Filter/Query/LimitedCountQueryBuilderTest.php | 12 +++++------- .../Decorator/LocationServiceDecoratorTest.php | 1 + 18 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index e047d8e954..e315f2598c 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -110,9 +110,11 @@ public function loadParentLocationsForDraftContent($contentId); */ public function copySubtree($sourceId, $destinationParentId); + /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getSubtreeSize(string $path /* ?int $limit = null */): int; /** diff --git a/src/contracts/Persistence/Filter/Content/Handler.php b/src/contracts/Persistence/Filter/Content/Handler.php index 1e7b097849..33c5cd5d3e 100644 --- a/src/contracts/Persistence/Filter/Content/Handler.php +++ b/src/contracts/Persistence/Filter/Content/Handler.php @@ -26,6 +26,7 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter /* ?int $limit = null */): int; } diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php index 4da15ae828..6363e31481 100644 --- a/src/contracts/Persistence/Filter/Location/Handler.php +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -26,6 +26,7 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter /* ?int $limit = null */): int; } diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index 91c5f592c6..14bf87af6f 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -547,6 +547,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index 025f836ab2..f0057ed1e1 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -289,11 +289,12 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - - return $this->innerService->count($filter, $languages, $limit); + + return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 1cf35d2f16..43f69e7e2a 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -85,21 +85,23 @@ public function loadParentLocationsForDraftContent( /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getLocationChildCount(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getLocationChildCount($location, $limit); + return $this->innerService->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count } /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getSubtreeSize($location, $limit); + return $this->innerService->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count } public function createLocation( @@ -173,11 +175,12 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - return $this->innerService->count($filter, $languages, $limit); + return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index a24401fc9a..e026ff0981 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -125,6 +125,7 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar * * @return int */ + // @phpstan-ignore parameter.notFound public function getLocationChildCount(Location $location /* ?int $limit = null */): int; /** @@ -134,6 +135,7 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * * * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int; /** @@ -280,6 +282,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index c0f4221337..eba6f23f37 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -267,7 +267,8 @@ public function getSubtreeSize(string $path /* ?int $limit = null */): int 'limit' => $limit, ]); - return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); + // @phpstan-ignore-next-line + return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); /* @phpstan-ignore arguments.count */ } /** diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index 45edc404db..4c82e025c9 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -266,6 +266,9 @@ public function getSubtreeChildrenDraftContentIds(int $sourceId): array return $statement->fetchFirstColumn(); } + /** + * @phpstan-param positive-int $limit + */ public function getSubtreeSize(string $path, ?int $limit = null): int { $query = $this->createNodeQueryBuilder([$this->dbPlatform->getCountExpression('node_id')]); diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index 51f923464a..8abc1c10cd 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -93,6 +93,9 @@ private function getDatabasePlatform(): AbstractPlatform } } + /** + * @phpstan-param positive-int $limit + */ public function count(FilteringCriterion $criterion, ?int $limit = null): int { $query = $this->buildQuery( diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php index 54a6a19d9d..e1cb392a8e 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php @@ -60,6 +60,9 @@ private function getDatabasePlatform(): AbstractPlatform } } + /** + * @phpstan-param positive-int $limit + */ public function count(FilteringCriterion $criterion, ?int $limit = null): int { $query = $this->buildQuery($criterion); diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index cc26ab1a71..65fcfdb3a2 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2716,6 +2716,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; @@ -2737,7 +2738,7 @@ public function count(Filter $filter, ?array $languages = null /*?int $limit = n $filter->andWithCriterion($permissionCriterion); } - return $this->contentFilteringHandler->count($filter, $limit); + return $this->contentFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 37dfe82afe..59018c5df1 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -383,14 +383,16 @@ public function getLocationChildCount(APILocation $location /*?int $limit = null /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getSubtreeSize(APILocation $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - + + // @phpstan-ignore arguments.count return $this->persistenceHandler->locationHandler()->getSubtreeSize( $location->getPathString(), - $limit - ); + $limit + ); } protected function buildLocationChildrenFilter(APILocation $location): Filter @@ -952,6 +954,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; @@ -973,7 +976,7 @@ public function count(Filter $filter, ?array $languages = null /* ?int $limit = $filter->andWithCriterion($permissionCriterion); } - return $this->locationFilteringHandler->count($filter, $limit); + return $this->locationFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count } /** diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index b2f024fd6e..a71badeaf1 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -300,15 +300,17 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), - $limit - ); + $limit + ); } } diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index 3f57f5daf3..5ee81bd4ea 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -108,17 +108,18 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getLocationChildCount($location, $limit); + return $this->service->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count } /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getSubtreeSize($location, $limit); + return $this->service->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count } public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location @@ -202,14 +203,16 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ + // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; + // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), - $limit + $limit ); } } diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 839ff2b307..f341fb2f94 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1125,10 +1125,11 @@ public function testGetLocationChildCountWithLimitation(): void // $locationId is the ID of an existing location $locationService = $this->getRepository()->getLocationService(); $location = $locationService->loadLocation($this->generateId('location', 5)); - $this->assertNotNull($location); $this->assertSame( 2, + // @phpstan-ignore arguments.count $locationService->getLocationChildCount( + $location, 2 ) @@ -3567,6 +3568,9 @@ public function testGetSubtreeSize(): Location $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertNotNull($location); + + // phpstan-ignore-next-line self::assertSame(1, $locationService->getSubtreeSize($location)); $this->createFolder(['eng-GB' => 'Child 1'], $location->id); @@ -3584,13 +3588,18 @@ public function testGetSubtreeSizeWithLimit(): Location $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertNotNull($location); + self::assertSame(1, $locationService->getSubtreeSize($location)); for ($i = 1; $i <= 10; ++$i) { $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); } + + // @phpstan-ignore arguments.count self::assertSame(3, $locationService->getSubtreeSize($location, 3)); + return $location; } @@ -3602,6 +3611,8 @@ public function testGetSubtreeSizeWithInvalidLimitThrowsExpectedError(): Locatio $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertNotNull($location); + self::assertSame(1, $locationService->getSubtreeSize($location)); for ($i = 1; $i <= 10; ++$i) { diff --git a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php index 81ab63cee0..901251d3ef 100644 --- a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php @@ -8,10 +8,9 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\Query; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; /** @@ -19,9 +18,6 @@ */ class LimitedCountQueryBuilderTest extends TestCase { - private Connection $connectionMock; - - private AbstractPlatform $platformMock; private LimitedCountQueryBuilder $limitedCountQueryBuilder; @@ -39,6 +35,8 @@ public function testWrapThrowsExceptionOnZeroLimit(): void $this->expectExceptionMessageMatches('/Limit must be greater than 0/'); $qb = $this->getDatabaseConnection()->createQueryBuilder(); + + // @phpstan-ignore argument.type $this->limitedCountQueryBuilder->wrap($qb, 'someField', 0); } diff --git a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php index b11940719f..c85ab307cd 100644 --- a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php @@ -155,6 +155,7 @@ public function testGetLocationChildCountDecorator() $serviceMock->expects($this->once())->method('getLocationChildCount')->with(...$parameters); + // @phpstan-ignore arguments.count $decoratedService->getLocationChildCount(...$parameters); } From 8c99daff2fad7ff6cb5ad0a56a09d92719d3c776 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Mon, 23 Jun 2025 08:36:50 +0100 Subject: [PATCH 16/23] IBX-10186 Run CS --- src/contracts/Persistence/Content/Location/Handler.php | 1 - .../Repository/Decorator/ContentServiceDecorator.php | 4 ++-- .../Repository/Decorator/LocationServiceDecorator.php | 6 +++--- src/lib/Persistence/Cache/LocationHandler.php | 2 +- src/lib/Repository/ContentService.php | 2 +- src/lib/Repository/LocationService.php | 10 +++++----- src/lib/Repository/SiteAccessAware/ContentService.php | 6 +++--- src/lib/Repository/SiteAccessAware/LocationService.php | 8 ++++---- .../Core/Repository/LocationServiceTest.php | 3 --- .../Filter/Query/LimitedCountQueryBuilderTest.php | 4 +--- 10 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index e315f2598c..671076ced9 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -110,7 +110,6 @@ public function loadParentLocationsForDraftContent($contentId); */ public function copySubtree($sourceId, $destinationParentId); - /** * @param int|null $limit */ diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index f0057ed1e1..819ca9383f 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -293,8 +293,8 @@ public function find(Filter $filter, ?array $languages = null): ContentList public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - - return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count + + return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index 43f69e7e2a..d1c9a81c08 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -90,7 +90,7 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count + return $this->innerService->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count } /** @@ -101,7 +101,7 @@ public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count + return $this->innerService->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count } public function createLocation( @@ -180,7 +180,7 @@ public function count(Filter $filter, ?array $languages = null /* ?int $limit = { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count + return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index eba6f23f37..f002cb23ed 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -268,7 +268,7 @@ public function getSubtreeSize(string $path /* ?int $limit = null */): int ]); // @phpstan-ignore-next-line - return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); /* @phpstan-ignore arguments.count */ + return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); /* @phpstan-ignore arguments.count */ } /** diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 65fcfdb3a2..0d7b0088fc 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2738,7 +2738,7 @@ public function count(Filter $filter, ?array $languages = null /*?int $limit = n $filter->andWithCriterion($permissionCriterion); } - return $this->contentFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count + return $this->contentFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count } } diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 59018c5df1..1dcbee04bb 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -387,12 +387,12 @@ public function getLocationChildCount(APILocation $location /*?int $limit = null public function getSubtreeSize(APILocation $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - - // @phpstan-ignore arguments.count + + // @phpstan-ignore arguments.count return $this->persistenceHandler->locationHandler()->getSubtreeSize( $location->getPathString(), - $limit - ); + $limit + ); } protected function buildLocationChildrenFilter(APILocation $location): Filter @@ -976,7 +976,7 @@ public function count(Filter $filter, ?array $languages = null /* ?int $limit = $filter->andWithCriterion($permissionCriterion); } - return $this->locationFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count + return $this->locationFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count } /** diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index a71badeaf1..4c7b167bc8 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -305,12 +305,12 @@ public function count(Filter $filter, ?array $languages = null /*?int $limit = n { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - // @phpstan-ignore arguments.count + // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), - $limit - ); + $limit + ); } } diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index 5ee81bd4ea..c72f47f626 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -108,7 +108,7 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count + return $this->service->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count } /** @@ -119,7 +119,7 @@ public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count + return $this->service->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count } public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location @@ -208,11 +208,11 @@ public function count(Filter $filter, ?array $languages = null /* ?int $limit = { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - // @phpstan-ignore arguments.count + // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), - $limit + $limit ); } } diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index f341fb2f94..23b6044a0b 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1129,7 +1129,6 @@ public function testGetLocationChildCountWithLimitation(): void 2, // @phpstan-ignore arguments.count $locationService->getLocationChildCount( - $location, 2 ) @@ -3596,10 +3595,8 @@ public function testGetSubtreeSizeWithLimit(): Location $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); } - // @phpstan-ignore arguments.count self::assertSame(3, $locationService->getSubtreeSize($location, 3)); - return $location; } diff --git a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php index 901251d3ef..067353bb94 100644 --- a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php @@ -8,17 +8,15 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\Query; - use Ibexa\Core\Base\Exceptions\InvalidArgumentException; -use Ibexa\Tests\Core\Persistence\Legacy\TestCase; use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * @covers \Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder */ class LimitedCountQueryBuilderTest extends TestCase { - private LimitedCountQueryBuilder $limitedCountQueryBuilder; protected function setUp(): void From 9d2d107d5d92fa38596d7efd360969ca50f1c16a Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Mon, 23 Jun 2025 11:40:51 +0100 Subject: [PATCH 17/23] IBX-10186 Fix exception type --- tests/integration/Core/Repository/LocationServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 23b6044a0b..a749311eb1 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -8,7 +8,6 @@ use Exception; use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\URLAliasService as URLAliasServiceInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Content; @@ -24,6 +23,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; /** * Test case for operations in the LocationService using in memory storage. From 80fe4534d42d98d1f8bb0e2f216179d09ab0a3fb Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 25 Jun 2025 15:41:44 +0100 Subject: [PATCH 18/23] IBX-10186 Fix merge issues --- tests/integration/Core/Repository/LocationServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index a749311eb1..2926a1ef9c 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -22,8 +22,8 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; -use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; /** * Test case for operations in the LocationService using in memory storage. From f25060d0d984b2d016f2cd7a790595efe6dedaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Fri, 27 Jun 2025 13:27:50 +0200 Subject: [PATCH 19/23] Regenerated PHPStan baseline --- phpstan-baseline.neon | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 04d7a82a65..41f82b4762 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -41823,7 +41823,7 @@ parameters: - message: '#^Cannot access property \$id on Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location\|null\.$#' identifier: property.nonObject - count: 11 + count: 9 path: tests/integration/Core/Repository/LocationServiceTest.php - @@ -42030,12 +42030,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/LocationServiceTest.php - - - message: '#^Method Ibexa\\Tests\\Integration\\Core\\Repository\\LocationServiceTest\:\:testGetSubtreeSize\(\) should return Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location but returns Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location\|null\.$#' - identifier: return.type - count: 1 - path: tests/integration/Core/Repository/LocationServiceTest.php - - message: '#^Method Ibexa\\Tests\\Integration\\Core\\Repository\\LocationServiceTest\:\:testHideLocation\(\) has no return type specified\.$#' identifier: missingType.return @@ -42300,12 +42294,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/LocationServiceTest.php - - - message: '#^Parameter \#1 \$location of method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getSubtreeSize\(\) expects Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location, Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location\|null given\.$#' - identifier: argument.type - count: 2 - path: tests/integration/Core/Repository/LocationServiceTest.php - - message: '#^Parameter \#1 \$location of method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:moveSubtree\(\) expects Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location, Ibexa\\Contracts\\Core\\Repository\\Values\\Content\\Location\|null given\.$#' identifier: argument.type From eb83b304808dff7f1acedd1b7eb51cd47b1bfd98 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 27 Jun 2025 20:36:12 +0100 Subject: [PATCH 20/23] IBX-10186 Update baseline --- phpstan-baseline.neon | 192 ++++++++++++++++++ .../Persistence/Filter/Content/Handler.php | 1 - .../Persistence/Filter/Location/Handler.php | 1 - src/contracts/Repository/ContentService.php | 1 - .../Decorator/ContentServiceDecorator.php | 3 +- .../Decorator/LocationServiceDecorator.php | 9 +- src/contracts/Repository/LocationService.php | 3 - src/lib/Persistence/Cache/LocationHandler.php | 3 +- src/lib/Repository/ContentService.php | 3 +- src/lib/Repository/LocationService.php | 5 +- .../SiteAccessAware/ContentService.php | 2 - .../SiteAccessAware/LocationService.php | 7 +- .../Core/Repository/LocationServiceTest.php | 2 - .../Query/LimitedCountQueryBuilderTest.php | 1 - .../LocationServiceDecoratorTest.php | 1 - 15 files changed, 201 insertions(+), 33 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 41f82b4762..125c096ecc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6492,6 +6492,12 @@ parameters: count: 1 path: src/contracts/Persistence/Content/VersionInfo.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/contracts/Persistence/Filter/Content/Handler.php + - message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Filter\\Doctrine\\FilteringQueryBuilder\:\:buildOperatorBasedCriterionConstraint\(\) has parameter \$criterionValue with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -6522,6 +6528,12 @@ parameters: count: 1 path: src/contracts/Persistence/Filter/LazyListIterator.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/contracts/Persistence/Filter/Location/Handler.php + - message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Handler\:\:beginTransaction\(\) has no return type specified\.$#' identifier: missingType.return @@ -6678,6 +6690,18 @@ parameters: count: 1 path: src/contracts/Repository/ContentService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\ContentService\:\:count\(\) invoked with 3 parameters, 1\-2 required\.$#' + identifier: arguments.count + count: 1 + path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + - message: '#^Method Ibexa\\Contracts\\Core\\Repository\\Decorator\\ContentServiceDecorator\:\:validate\(\) has parameter \$context with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -6690,12 +6714,42 @@ parameters: count: 1 path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + - message: '#^Method Ibexa\\Contracts\\Core\\Repository\\Decorator\\LocationServiceDecorator\:\:loadLocationList\(\) has parameter \$locationIds with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:count\(\) invoked with 3 parameters, 1\-2 required\.$#' + identifier: arguments.count + count: 1 + path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getLocationChildCount\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 3 + path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + - message: '#^Method Ibexa\\Contracts\\Core\\Repository\\Decorator\\SearchServiceDecorator\:\:suggest\(\) has no return type specified\.$#' identifier: missingType.return @@ -8280,6 +8334,12 @@ parameters: count: 1 path: src/contracts/Repository/LocationService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 3 + path: src/contracts/Repository/LocationService.php + - message: '#^PHPDoc tag @param for parameter \$objectStateGroupId with type mixed is not subtype of native type int\.$#' identifier: parameter.phpDocType @@ -17388,6 +17448,12 @@ parameters: count: 1 path: src/lib/Persistence/Cache/ContentTypeHandler.php + - + message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\Handler\:\:beginTransaction\(\) has no return type specified\.$#' identifier: missingType.return @@ -17478,6 +17544,12 @@ parameters: count: 1 path: src/lib/Persistence/Cache/LocationHandler.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Content\\Location\\Handler\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\LocationHandler\:\:changeMainLocation\(\) has no return type specified\.$#' identifier: missingType.return @@ -17724,6 +17796,18 @@ parameters: count: 1 path: src/lib/Persistence/Cache/SectionHandler.php + - + message: '#^PHPDoc tag @throws with type Psr\\Cache\\CacheException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: src/lib/Persistence/Cache/SettingHandler.php + + - + message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 2 + path: src/lib/Persistence/Cache/SettingHandler.php + - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\TransactionHandler\:\:beginTransaction\(\) has no return type specified\.$#' identifier: missingType.return @@ -17826,6 +17910,12 @@ parameters: count: 1 path: src/lib/Persistence/Cache/UrlAliasHandler.php + - + message: '#^PHPDoc tag @throws with type Ibexa\\Core\\Base\\Exceptions\\BadStateException\|Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\UrlWildcardHandler\:\:find\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -17838,6 +17928,12 @@ parameters: count: 1 path: src/lib/Persistence/Cache/UrlWildcardHandler.php + - + message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: src/lib/Persistence/Cache/UrlWildcardHandler.php + - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\UserHandler\:\:assignRole\(\) has no return type specified\.$#' identifier: missingType.return @@ -24234,6 +24330,12 @@ parameters: count: 1 path: src/lib/Repository/ContentService.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Filter\\Content\\Handler\:\:count\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/ContentService.php + - message: '#^Method Ibexa\\Core\\Repository\\ContentService\:\:__construct\(\) has parameter \$settings with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -24294,6 +24396,12 @@ parameters: count: 1 path: src/lib/Repository/ContentService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/lib/Repository/ContentService.php + - message: '#^PHPDoc tag @var has invalid value \(\$content \\Ibexa\\Core\\Repository\\Values\\Content\\Content\)\: Unexpected token "\$content", expected type at offset 9 on line 1$#' identifier: phpDoc.parseError @@ -24552,12 +24660,24 @@ parameters: count: 1 path: src/lib/Repository/LocationService.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Content\\Location\\Handler\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/LocationService.php + - message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Content\\UrlAlias\\Handler\:\:publishUrlAliasForLocation\(\) invoked with 6 parameters, 4\-5 required\.$#' identifier: arguments.count count: 1 path: src/lib/Repository/LocationService.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Persistence\\Filter\\Location\\Handler\:\:count\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/LocationService.php + - message: '#^Method Ibexa\\Core\\Repository\\LocationService\:\:__construct\(\) has parameter \$settings with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -24570,6 +24690,12 @@ parameters: count: 1 path: src/lib/Repository/LocationService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 2 + path: src/lib/Repository/LocationService.php + - message: '#^Property Ibexa\\Core\\Repository\\LocationService\:\:\$repository \(Ibexa\\Core\\Repository\\Repository\) does not accept Ibexa\\Contracts\\Core\\Repository\\Repository\.$#' identifier: assign.propertyType @@ -25512,6 +25638,12 @@ parameters: count: 1 path: src/lib/Repository/SettingService.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\ContentService\:\:count\(\) invoked with 3 parameters, 1\-2 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/SiteAccessAware/ContentService.php + - message: '#^Method Ibexa\\Core\\Repository\\SiteAccessAware\\ContentService\:\:validate\(\) has parameter \$context with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -25524,6 +25656,12 @@ parameters: count: 1 path: src/lib/Repository/SiteAccessAware/ContentService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 1 + path: src/lib/Repository/SiteAccessAware/ContentService.php + - message: '#^Method Ibexa\\Core\\Repository\\SiteAccessAware\\Language\\AbstractLanguageResolver\:\:getPrioritizedLanguages\(\) has parameter \$forcedLanguages with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -25542,12 +25680,36 @@ parameters: count: 1 path: src/lib/Repository/SiteAccessAware/Language/LanguageResolver.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:count\(\) invoked with 3 parameters, 1\-2 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/SiteAccessAware/LocationService.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getLocationChildCount\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/SiteAccessAware/LocationService.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: src/lib/Repository/SiteAccessAware/LocationService.php + - message: '#^Method Ibexa\\Core\\Repository\\SiteAccessAware\\LocationService\:\:loadLocationList\(\) has parameter \$locationIds with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: src/lib/Repository/SiteAccessAware/LocationService.php + - + message: '#^PHPDoc tag @param references unknown parameter\: \$limit$#' + identifier: parameter.notFound + count: 2 + path: src/lib/Repository/SiteAccessAware/LocationService.php + - message: '#^Property Ibexa\\Core\\Repository\\SiteAccessAware\\Repository\:\:\$notificationService \(Ibexa\\Core\\Repository\\NotificationService\) does not accept Ibexa\\Core\\Repository\\SiteAccessAware\\NotificationService\.$#' identifier: assign.propertyType @@ -41832,6 +41994,18 @@ parameters: count: 2 path: tests/integration/Core/Repository/LocationServiceTest.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getLocationChildCount\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + - message: '#^Method Ibexa\\Tests\\Integration\\Core\\Repository\\LocationServiceTest\:\:assertAliasesBeforeCopy\(\) has no return type specified\.$#' identifier: missingType.return @@ -59424,6 +59598,12 @@ parameters: count: 2 path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + - + message: '#^Method Ibexa\\Tests\\Core\\MVC\\Symfony\\Templating\\Twig\\Extension\\FileSystemTwigIntegrationTestCase\:\:doIntegrationTest\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + - message: '#^Method Ibexa\\Tests\\Core\\MVC\\Symfony\\Templating\\Twig\\Extension\\FileSystemTwigIntegrationTestCase\:\:doIntegrationTest\(\) has parameter \$condition with no type specified\.$#' identifier: missingType.parameter @@ -67158,6 +67338,12 @@ parameters: count: 1 path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php + - + message: '#^Parameter \#3 \$limit of method Ibexa\\Core\\Persistence\\Legacy\\Filter\\Query\\LimitedCountQueryBuilder\:\:wrap\(\) expects int\<1, max\>\|null, 0 given\.$#' + identifier: argument.type + count: 1 + path: tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php + - message: '#^Call to an undefined method Ibexa\\Contracts\\Core\\Container\:\:get\(\)\.$#' identifier: method.notFound @@ -68958,6 +69144,12 @@ parameters: count: 1 path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + - + message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getLocationChildCount\(\) invoked with 2 parameters, 1 required\.$#' + identifier: arguments.count + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + - message: '#^Method Ibexa\\Tests\\Core\\Repository\\Decorator\\LocationServiceDecoratorTest\:\:testCopySubtreeDecorator\(\) has no return type specified\.$#' identifier: missingType.return diff --git a/src/contracts/Persistence/Filter/Content/Handler.php b/src/contracts/Persistence/Filter/Content/Handler.php index 33c5cd5d3e..1e7b097849 100644 --- a/src/contracts/Persistence/Filter/Content/Handler.php +++ b/src/contracts/Persistence/Filter/Content/Handler.php @@ -26,7 +26,6 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter /* ?int $limit = null */): int; } diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php index 6363e31481..4da15ae828 100644 --- a/src/contracts/Persistence/Filter/Location/Handler.php +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -26,7 +26,6 @@ public function find(Filter $filter): iterable; * @param \Ibexa\Contracts\Core\Repository\Values\Filter\Filter $filter * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter /* ?int $limit = null */): int; } diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index 14bf87af6f..91c5f592c6 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -547,7 +547,6 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index 819ca9383f..025f836ab2 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -289,12 +289,11 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php index d1c9a81c08..1cf35d2f16 100644 --- a/src/contracts/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -85,23 +85,21 @@ public function loadParentLocationsForDraftContent( /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function getLocationChildCount(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count + return $this->innerService->getLocationChildCount($location, $limit); } /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->innerService->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count + return $this->innerService->getSubtreeSize($location, $limit); } public function createLocation( @@ -175,12 +173,11 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - return $this->innerService->count($filter, $languages, $limit); // @phpstan-ignore arguments.count + return $this->innerService->count($filter, $languages, $limit); } } diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index e026ff0981..a24401fc9a 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -125,7 +125,6 @@ public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?ar * * @return int */ - // @phpstan-ignore parameter.notFound public function getLocationChildCount(Location $location /* ?int $limit = null */): int; /** @@ -135,7 +134,6 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * * * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int; /** @@ -282,7 +280,6 @@ public function find(Filter $filter, ?array $languages = null): LocationList; * @param int|null $limit If set, the count will be limited to first $limit items found. * In some cases it can significantly speed up a count operation for more complex filters. */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int; } diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index f002cb23ed..c0f4221337 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -267,8 +267,7 @@ public function getSubtreeSize(string $path /* ?int $limit = null */): int 'limit' => $limit, ]); - // @phpstan-ignore-next-line - return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); /* @phpstan-ignore arguments.count */ + return $this->persistenceHandler->locationHandler()->getSubtreeSize($path, $limit); } /** diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 0d7b0088fc..cc26ab1a71 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -2716,7 +2716,6 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; @@ -2738,7 +2737,7 @@ public function count(Filter $filter, ?array $languages = null /*?int $limit = n $filter->andWithCriterion($permissionCriterion); } - return $this->contentFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count + return $this->contentFilteringHandler->count($filter, $limit); } } diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php index 1dcbee04bb..37dfe82afe 100644 --- a/src/lib/Repository/LocationService.php +++ b/src/lib/Repository/LocationService.php @@ -383,12 +383,10 @@ public function getLocationChildCount(APILocation $location /*?int $limit = null /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function getSubtreeSize(APILocation $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - // @phpstan-ignore arguments.count return $this->persistenceHandler->locationHandler()->getSubtreeSize( $location->getPathString(), $limit @@ -954,7 +952,6 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; @@ -976,7 +973,7 @@ public function count(Filter $filter, ?array $languages = null /* ?int $limit = $filter->andWithCriterion($permissionCriterion); } - return $this->locationFilteringHandler->count($filter, $limit); // @phpstan-ignore arguments.count + return $this->locationFilteringHandler->count($filter, $limit); } /** diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index 4c7b167bc8..b2f024fd6e 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -300,12 +300,10 @@ public function find(Filter $filter, ?array $languages = null): ContentList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /*?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php index c72f47f626..3f57f5daf3 100644 --- a/src/lib/Repository/SiteAccessAware/LocationService.php +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -108,18 +108,17 @@ public function getLocationChildCount(Location $location /* ?int $limit = null * { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getLocationChildCount($location, $limit); // @phpstan-ignore arguments.count + return $this->service->getLocationChildCount($location, $limit); } /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function getSubtreeSize(Location $location /* ?int $limit = null */): int { $limit = func_num_args() > 1 ? func_get_arg(1) : null; - return $this->service->getSubtreeSize($location, $limit); // @phpstan-ignore arguments.count + return $this->service->getSubtreeSize($location, $limit); } public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location @@ -203,12 +202,10 @@ public function find(Filter $filter, ?array $languages = null): LocationList /** * @param int|null $limit */ - // @phpstan-ignore parameter.notFound public function count(Filter $filter, ?array $languages = null /* ?int $limit = null */): int { $limit = func_num_args() > 2 ? func_get_arg(2) : null; - // @phpstan-ignore arguments.count return $this->service->count( $filter, $this->languageResolver->getPrioritizedLanguages($languages), diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index 2926a1ef9c..ba8f5853df 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -1127,7 +1127,6 @@ public function testGetLocationChildCountWithLimitation(): void $location = $locationService->loadLocation($this->generateId('location', 5)); $this->assertSame( 2, - // @phpstan-ignore arguments.count $locationService->getLocationChildCount( $location, 2 @@ -3595,7 +3594,6 @@ public function testGetSubtreeSizeWithLimit(): Location $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); } - // @phpstan-ignore arguments.count self::assertSame(3, $locationService->getSubtreeSize($location, 3)); return $location; diff --git a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php index 067353bb94..0ae64145db 100644 --- a/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/Query/LimitedCountQueryBuilderTest.php @@ -34,7 +34,6 @@ public function testWrapThrowsExceptionOnZeroLimit(): void $qb = $this->getDatabaseConnection()->createQueryBuilder(); - // @phpstan-ignore argument.type $this->limitedCountQueryBuilder->wrap($qb, 'someField', 0); } diff --git a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php index c85ab307cd..b11940719f 100644 --- a/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php @@ -155,7 +155,6 @@ public function testGetLocationChildCountDecorator() $serviceMock->expects($this->once())->method('getLocationChildCount')->with(...$parameters); - // @phpstan-ignore arguments.count $decoratedService->getLocationChildCount(...$parameters); } From 500b40000f0d8f2c55caa9ab101c781b6f70d0c0 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 27 Jun 2025 20:56:48 +0100 Subject: [PATCH 21/23] IBX-10186 Update exception type --- tests/integration/Core/Repository/LocationServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index ba8f5853df..a4918ac2a6 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -22,7 +22,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; -use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; /** From 2c1b555d47e00611519b3dc9e92ad0f5e63cb4b8 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 27 Jun 2025 21:15:19 +0100 Subject: [PATCH 22/23] IBX-10186 Run everything like 20 times and to not play wack-a-mole with errors --- phpstan-baseline.neon | 8 +++++++- tests/integration/Core/Repository/LocationServiceTest.php | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 125c096ecc..a951a909b5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4068,6 +4068,12 @@ parameters: count: 1 path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + - + message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + - message: '#^Property Ibexa\\Bundle\\Core\\Imagine\\Cache\\AliasGeneratorDecorator\:\:\$siteAccess \(Ibexa\\Core\\MVC\\Symfony\\SiteAccess\) does not accept Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#' identifier: assign.propertyType @@ -42003,7 +42009,7 @@ parameters: - message: '#^Method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:getSubtreeSize\(\) invoked with 2 parameters, 1 required\.$#' identifier: arguments.count - count: 1 + count: 2 path: tests/integration/Core/Repository/LocationServiceTest.php - diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index a4918ac2a6..c4464034f8 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -8,6 +8,7 @@ use Exception; use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\URLAliasService as URLAliasServiceInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Content; @@ -22,7 +23,6 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; /** @@ -3588,8 +3588,6 @@ public function testGetSubtreeSizeWithLimit(): Location $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); self::assertNotNull($location); - self::assertSame(1, $locationService->getSubtreeSize($location)); - for ($i = 1; $i <= 10; ++$i) { $this->createFolder(['eng-GB' => 'Child ' . $i], $location->id); } @@ -3617,6 +3615,8 @@ public function testGetSubtreeSizeWithInvalidLimitThrowsExpectedError(): Locatio $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessageRegExp('/Limit must be greater than 0/'); + self::assertSame(3, $locationService->getSubtreeSize($location, -42)); + return $location; } From a54d9225bd364425bdb4d4a228242bc6660a135d Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 28 Jun 2025 01:25:38 +0100 Subject: [PATCH 23/23] IBX-10186 Fix PSR deciding to update their libs --- phpstan-baseline.neon | 42 ------------------- .../Core/Repository/LocationServiceTest.php | 2 +- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a951a909b5..5b04035574 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4068,12 +4068,6 @@ parameters: count: 1 path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php - - - message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php - - message: '#^Property Ibexa\\Bundle\\Core\\Imagine\\Cache\\AliasGeneratorDecorator\:\:\$siteAccess \(Ibexa\\Core\\MVC\\Symfony\\SiteAccess\) does not accept Ibexa\\Core\\MVC\\Symfony\\SiteAccess\|null\.$#' identifier: assign.propertyType @@ -17454,12 +17448,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/ContentTypeHandler.php - - - message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: src/lib/Persistence/Cache/ContentTypeHandler.php - - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\Handler\:\:beginTransaction\(\) has no return type specified\.$#' identifier: missingType.return @@ -17802,18 +17790,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/SectionHandler.php - - - message: '#^PHPDoc tag @throws with type Psr\\Cache\\CacheException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: src/lib/Persistence/Cache/SettingHandler.php - - - - message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 2 - path: src/lib/Persistence/Cache/SettingHandler.php - - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\TransactionHandler\:\:beginTransaction\(\) has no return type specified\.$#' identifier: missingType.return @@ -17916,12 +17892,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/UrlAliasHandler.php - - - message: '#^PHPDoc tag @throws with type Ibexa\\Core\\Base\\Exceptions\\BadStateException\|Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: src/lib/Persistence/Cache/UrlAliasHandler.php - - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\UrlWildcardHandler\:\:find\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -17934,12 +17904,6 @@ parameters: count: 1 path: src/lib/Persistence/Cache/UrlWildcardHandler.php - - - message: '#^PHPDoc tag @throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: src/lib/Persistence/Cache/UrlWildcardHandler.php - - message: '#^Method Ibexa\\Core\\Persistence\\Cache\\UserHandler\:\:assignRole\(\) has no return type specified\.$#' identifier: missingType.return @@ -59604,12 +59568,6 @@ parameters: count: 2 path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php - - - message: '#^Method Ibexa\\Tests\\Core\\MVC\\Symfony\\Templating\\Twig\\Extension\\FileSystemTwigIntegrationTestCase\:\:doIntegrationTest\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php - - message: '#^Method Ibexa\\Tests\\Core\\MVC\\Symfony\\Templating\\Twig\\Extension\\FileSystemTwigIntegrationTestCase\:\:doIntegrationTest\(\) has parameter \$condition with no type specified\.$#' identifier: missingType.parameter diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php index c4464034f8..c1d3f28a8c 100644 --- a/tests/integration/Core/Repository/LocationServiceTest.php +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -3613,7 +3613,7 @@ public function testGetSubtreeSizeWithInvalidLimitThrowsExpectedError(): Locatio } $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageRegExp('/Limit must be greater than 0/'); + $this->expectExceptionMessageMatches('/Limit must be greater than 0/'); self::assertSame(3, $locationService->getSubtreeSize($location, -42));