From aa3106487c8d2d9fb3b1fc7a19b92a1499bc8ea8 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 27 Nov 2024 14:02:34 +0100 Subject: [PATCH 01/27] [Composer] Bumped pagerfanta/pagerfanta requirement to ^3.6.2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index de1baa383f..a37b1b5106 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "liip/imagine-bundle": "^2.3", "nelmio/cors-bundle": "^2.0", "oneup/flysystem-bundle": "^4.4.2", - "pagerfanta/pagerfanta": "^2.1", + "pagerfanta/pagerfanta": "^3.6.2", "psr/event-dispatcher": "^1.0", "sensio/framework-extra-bundle": "^6.1", "symfony-cmf/routing": "^3.0", From ae211053dbd41098158f4fe62528586902b2eff8 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 3 Dec 2024 14:32:18 +0100 Subject: [PATCH 02/27] [Pagerfanta] Aligned adapters with v3 --- src/contracts/Repository/ContentService.php | 2 + .../Repository/Values/Content/ContentList.php | 3 + .../Values/Content/Search/SearchHit.php | 4 +- .../Values/Content/Search/SearchResult.php | 6 +- .../AbstractSearchResultAdapter.php | 56 +++++++++---------- .../Pagerfanta/ContentFilteringAdapter.php | 26 ++++++--- .../Pagerfanta/ContentSearchAdapter.php | 56 ++++++++++++++++--- .../Pagerfanta/ContentSearchHitAdapter.php | 5 ++ .../Pagerfanta/LocationSearchAdapter.php | 56 ++++++++++++++++--- .../Pagerfanta/LocationSearchHitAdapter.php | 4 ++ .../Pagerfanta/SearchResultAdapter.php | 4 ++ 11 files changed, 167 insertions(+), 55 deletions(-) diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index be8943d8a3..9b744b855c 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -525,6 +525,8 @@ 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. + * + * @phpstan-return int<0, max> */ public function count(Filter $filter, ?array $languages = null): int; } diff --git a/src/contracts/Repository/Values/Content/ContentList.php b/src/contracts/Repository/Values/Content/ContentList.php index cef8cc4e73..4256dd114d 100644 --- a/src/contracts/Repository/Values/Content/ContentList.php +++ b/src/contracts/Repository/Values/Content/ContentList.php @@ -34,6 +34,9 @@ public function __construct(int $totalCount, array $contentItems) $this->contentItems = $contentItems; } + /** + * @phpstan-return int<0, max> + */ public function getTotalCount(): int { return $this->totalCount; diff --git a/src/contracts/Repository/Values/Content/Search/SearchHit.php b/src/contracts/Repository/Values/Content/Search/SearchHit.php index cf7004ed17..f41431ebdb 100644 --- a/src/contracts/Repository/Values/Content/Search/SearchHit.php +++ b/src/contracts/Repository/Values/Content/Search/SearchHit.php @@ -12,13 +12,15 @@ /** * This class represents a SearchHit matching the query. + * + * @template-covariant T of \Ibexa\Contracts\Core\Repository\Values\ValueObject */ class SearchHit extends ValueObject { /** * The value found by the search. * - * @var \Ibexa\Contracts\Core\Repository\Values\ValueObject + * @phpstan-var T */ public $valueObject; diff --git a/src/contracts/Repository/Values/Content/Search/SearchResult.php b/src/contracts/Repository/Values/Content/Search/SearchResult.php index e495eed7eb..4e04f21a4d 100644 --- a/src/contracts/Repository/Values/Content/Search/SearchResult.php +++ b/src/contracts/Repository/Values/Content/Search/SearchResult.php @@ -15,6 +15,8 @@ /** * This class represents a search result. + * + * @phpstan-implements \IteratorAggregate */ class SearchResult extends ValueObject implements IteratorAggregate, AggregationResultAwareInterface { @@ -77,9 +79,9 @@ class SearchResult extends ValueObject implements IteratorAggregate, Aggregation * * `null` if Query->performCount was set to false and search engine avoids search lookup. * - * @var int|null + * @phpstan-var int<0, max>|null */ - public $totalCount; + public ?int $totalCount; public function __construct(array $properties = []) { diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php index 2817deba58..5d82d4fc12 100644 --- a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -15,34 +15,39 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SpellcheckResult; use Pagerfanta\Adapter\AdapterInterface; +/** + * @phpstan-type TSearchLanguageFilter array{languages?: string[], useAlwaysAvailable?: bool} + * + * @template T of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + * @phpstan-implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + */ abstract class AbstractSearchResultAdapter implements AdapterInterface, SearchResultAdapter { - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query */ - private $query; + private Query $query; - /** @var array */ - private $languageFilter; + /** @phpstan-var TSearchLanguageFilter */ + private array $languageFilter; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection|null */ - private $aggregations; + private ?AggregationResultCollection $aggregations; - /** @var float|null */ - private $time; + private ?float $time; - /** @var bool|null */ - private $timedOut; + private ?bool $timedOut; - /** @var float|null */ - private $maxScore; + private ?float $maxScore; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount; private ?SpellcheckResult $spellcheck = null; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ public function __construct(Query $query, SearchService $searchService, array $languageFilter = []) { $this->query = $query; @@ -50,12 +55,7 @@ public function __construct(Query $query, SearchService $searchService, array $l $this->languageFilter = $languageFilter; } - /** - * Returns the number of results. - * - * @return int The number of results. - */ - public function getNbResults() + public function getNbResults(): int { if (isset($this->totalCount)) { return $this->totalCount; @@ -74,18 +74,13 @@ public function getNbResults() $this->languageFilter ); - return $this->totalCount = $searchResults->totalCount; + return $this->totalCount = $searchResults->totalCount ?? 0; } /** * Returns a slice of the results, as SearchHit objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $query = clone $this->query; $query->offset = $offset; @@ -167,6 +162,9 @@ public function getMaxScore(): ?float return $this->maxScore; } + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ abstract protected function executeQuery( SearchService $searchService, Query $query, diff --git a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php index 6a4f6f413e..2a25ae0b9c 100644 --- a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php @@ -14,21 +14,26 @@ /** * Pagerfanta adapter for content filtering. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * + * @phpstan-type TFilteringLanguageFilter array|null */ final class ContentFilteringAdapter implements AdapterInterface { - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; + private ContentService $contentService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\Filter */ - private $filter; + private Filter $filter; - /** @var array|null */ - private $languageFilter; + /** @var TFilteringLanguageFilter */ + private ?array $languageFilter; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount; + /** + * @param array|null $languageFilter + */ public function __construct( ContentService $contentService, Filter $filter, @@ -48,6 +53,11 @@ public function getNbResults(): int return $this->totalCount; } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentList + */ public function getSlice($offset, $length): iterable { $selectFilter = clone $this->filter; diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php index 2f50492f11..05e9e214b0 100644 --- a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php @@ -7,27 +7,67 @@ namespace Ibexa\Core\Pagination\Pagerfanta; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Pagerfanta\Adapter\AdapterInterface; + /** * Pagerfanta adapter for Ibexa content search. * Will return results as Content objects. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter */ -class ContentSearchAdapter extends ContentSearchHitAdapter +class ContentSearchAdapter implements AdapterInterface, SearchResultAdapter { + private ContentSearchHitAdapter $contentSearchHitAdapter; + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ + public function __construct(Query $query, SearchService $searchService, array $languageFilter = []) + { + $this->contentSearchHitAdapter = new ContentSearchHitAdapter($query, $searchService, $languageFilter); + } + /** * Returns a slice of the results as Content objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { + foreach ($this->contentSearchHitAdapter->getSlice($offset, $length) as $hit) { $list[] = $hit->valueObject; } return $list; } + + public function getNbResults(): int + { + return $this->contentSearchHitAdapter->getNbResults(); + } + + public function getAggregations(): AggregationResultCollection + { + return $this->contentSearchHitAdapter->getAggregations(); + } + + public function getTime(): ?float + { + return $this->contentSearchHitAdapter->getTime(); + } + + public function getTimedOut(): ?bool + { + return $this->contentSearchHitAdapter->getTimedOut(); + } + + public function getMaxScore(): ?float + { + return $this->contentSearchHitAdapter->getMaxScore(); + } } diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php index 00e5b02b83..ab06fa34c0 100644 --- a/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php @@ -14,9 +14,14 @@ /** * Pagerfanta adapter for Ibexa content search. * Will return results as SearchHit objects. + * + * @extends \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ class ContentSearchHitAdapter extends AbstractSearchResultAdapter { + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ protected function executeQuery( SearchService $searchService, Query $query, diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php index 1b35248a74..755365c3b9 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php @@ -7,27 +7,69 @@ namespace Ibexa\Core\Pagination\Pagerfanta; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Pagerfanta\Adapter\AdapterInterface; + /** * Pagerfanta adapter for Ibexa content search. * Will return results as Location objects. + * + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter */ -class LocationSearchAdapter extends LocationSearchHitAdapter +class LocationSearchAdapter implements AdapterInterface, SearchResultAdapter { + private LocationSearchHitAdapter $locationSearchHitAdapter; + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ + public function __construct(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + { + $this->locationSearchHitAdapter = new LocationSearchHitAdapter($query, $searchService, $languageFilter); + } + /** * Returns a slice of the results as Location objects. * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + * @phpstan-return iterable, \Ibexa\Contracts\Core\Repository\Values\Content\Location> */ - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { + foreach ($this->locationSearchHitAdapter->getSlice($offset, $length) as $hit) { $list[] = $hit->valueObject; } return $list; } + + public function getNbResults(): int + { + return $this->locationSearchHitAdapter->getNbResults(); + } + + public function getAggregations(): AggregationResultCollection + { + return $this->locationSearchHitAdapter->getAggregations(); + } + + public function getTime(): ?float + { + return $this->locationSearchHitAdapter->getTime(); + } + + public function getTimedOut(): ?bool + { + return $this->locationSearchHitAdapter->getTimedOut(); + } + + public function getMaxScore(): ?float + { + return $this->locationSearchHitAdapter->getMaxScore(); + } } diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php index 636a1f402c..0e4a5a00f6 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php @@ -15,6 +15,8 @@ /** * Pagerfanta adapter for Ibexa location search. * Will return results as SearchHit objects. + * + * @extends \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> */ class LocationSearchHitAdapter extends AbstractSearchResultAdapter { @@ -25,6 +27,8 @@ public function __construct(LocationQuery $query, SearchService $searchService, /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function executeQuery(SearchService $searchService, Query $query, array $languageFilter): SearchResult { diff --git a/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php index 5b0acd0acf..42603a01e6 100644 --- a/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php @@ -15,6 +15,10 @@ * Contract for SearchService based adapters. * * @see \Ibexa\Contracts\Core\Repository\SearchService + * + * @template-covariant T + * + * @extends \Pagerfanta\Adapter\AdapterInterface */ interface SearchResultAdapter extends AdapterInterface { From b9ed573db921fe62de91b3112ab4cb75520d37e6 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 3 Dec 2024 14:41:02 +0100 Subject: [PATCH 03/27] [Pagerfanta] Aligned CopySubtreeCommand with totalCount changes and improved code quality --- .../Core/Command/CopySubtreeCommand.php | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/bundle/Core/Command/CopySubtreeCommand.php b/src/bundle/Core/Command/CopySubtreeCommand.php index a8270b1d55..bd4b809b5a 100644 --- a/src/bundle/Core/Command/CopySubtreeCommand.php +++ b/src/bundle/Core/Command/CopySubtreeCommand.php @@ -32,20 +32,19 @@ class CopySubtreeCommand extends Command protected static $defaultDescription = 'Copies a subtree from one Location to another'; - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; + private LocationService $locationService; /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ - private $permissionResolver; + private PermissionResolver $permissionResolver; /** @var \Ibexa\Contracts\Core\Repository\UserService */ - private $userService; + private UserService $userService; /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ - private $contentTypeService; + private ContentTypeService $contentTypeService; /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; /** * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService @@ -69,7 +68,7 @@ public function __construct( $this->searchService = $searchService; } - protected function configure() + protected function configure(): void { $this ->addArgument( @@ -95,9 +94,10 @@ protected function configure() * @param \Symfony\Component\Console\Input\InputInterface $input * @param \Symfony\Component\Console\Output\OutputInterface $output * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - protected function initialize(InputInterface $input, OutputInterface $output) + protected function initialize(InputInterface $input, OutputInterface $output): void { parent::initialize($input, $output); $this->permissionResolver->setCurrentUserReference( @@ -106,11 +106,6 @@ protected function initialize(InputInterface $input, OutputInterface $output) } /** - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * - * @return int|null - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException @@ -144,9 +139,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $questionHelper = $this->getHelper('question'); $question = new ConfirmationQuestion( sprintf( - 'Are you sure you want to copy `%s` subtree (no. of children: %d) into `%s`? This may take a while for a big number of nested children [Y/n]? ', + 'Are you sure you want to copy `%s` subtree (no. of children: %s) into `%s`? This may take a while for a big number of nested children [Y/n]? ', $sourceLocation->contentInfo->name, - $this->getAllChildrenCount($sourceLocation), + $this->getAllChildrenCountExpr($sourceLocation), $targetLocation->contentInfo->name ) ); @@ -168,20 +163,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * - * @return int - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - protected function getAllChildrenCount(Location $location): int + protected function getAllChildrenCountExpr(Location $location): string { $query = new LocationQuery([ 'filter' => new Criterion\Subtree($location->pathString), ]); - $searchResults = $this->searchService->findLocations($query); + $totalCount = $this->searchService->findLocations($query)->totalCount; - return $searchResults->totalCount; + return $totalCount !== null ? (string) $totalCount : '∞'; } } From 7b700bed02494a98d1237d4d0f17ed20624796c8 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:31:29 +0100 Subject: [PATCH 04/27] [Pagerfanta][PHPStan] Declared template on `Content\Search\SearchResult` --- .../Core/Command/ResizeOriginalImagesCommand.php | 7 ++----- src/bundle/Core/Features/Context/UserContext.php | 15 ++++++++------- .../Values/Content/Search/SearchResult.php | 6 ++++-- .../Pagerfanta/AbstractSearchResultAdapter.php | 2 ++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/bundle/Core/Command/ResizeOriginalImagesCommand.php b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php index 649beac29c..4c219a26ac 100644 --- a/src/bundle/Core/Command/ResizeOriginalImagesCommand.php +++ b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php @@ -210,7 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int while ($query->offset <= $totalCount) { $results = $this->searchService->findContent($query); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $hit */ foreach ($results->searchHits as $hit) { $this->resize($output, $hit, $imageFieldIdentifier, $filter); $progressBar->advance(); @@ -232,10 +232,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param \Symfony\Component\Console\Output\OutputInterface $output - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit - * @param string $imageFieldIdentifier - * @param string $filter + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $hit */ private function resize(OutputInterface $output, SearchHit $hit, string $imageFieldIdentifier, string $filter): void { diff --git a/src/bundle/Core/Features/Context/UserContext.php b/src/bundle/Core/Features/Context/UserContext.php index 3d2bc860a6..7146119724 100644 --- a/src/bundle/Core/Features/Context/UserContext.php +++ b/src/bundle/Core/Features/Context/UserContext.php @@ -82,26 +82,27 @@ public function searchUserByLogin($username, $parentGroupId = null) * Search User Groups with given name. * * @param string $name name of User Group to search for - * @param string $parentLocationId (optional) parent location id to search in + * @param int|null $parentLocationId (optional) parent location id to search in * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] search results + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\Content\Content>> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function searchUserGroups($name, $parentLocationId = null) + public function searchUserGroups(string $name, ?int $parentLocationId = null): array { $criterionArray = [ new Criterion\Subtree(self::USERGROUP_ROOT_SUBTREE), new Criterion\ContentTypeIdentifier(self::USERGROUP_CONTENT_IDENTIFIER), new Criterion\Field('name', Criterion\Operator::EQ, $name), ]; - if ($parentLocationId) { + if (null !== $parentLocationId) { $criterionArray[] = new Criterion\ParentLocationId($parentLocationId); } $query = new Query(); $query->filter = new Criterion\LogicalAnd($criterionArray); - $result = $this->searchService->findContent($query, [], false); - - return $result->searchHits; + return $this->searchService->findContent($query, [], false)->searchHits; } /** diff --git a/src/contracts/Repository/Values/Content/Search/SearchResult.php b/src/contracts/Repository/Values/Content/Search/SearchResult.php index 4e04f21a4d..c122cea691 100644 --- a/src/contracts/Repository/Values/Content/Search/SearchResult.php +++ b/src/contracts/Repository/Values/Content/Search/SearchResult.php @@ -16,6 +16,8 @@ /** * This class represents a search result. * + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * * @phpstan-implements \IteratorAggregate */ class SearchResult extends ValueObject implements IteratorAggregate, AggregationResultAwareInterface @@ -37,9 +39,9 @@ class SearchResult extends ValueObject implements IteratorAggregate, Aggregation /** * The value objects found for the query. * - * @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] + * @phpstan-var list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> */ - public $searchHits = []; + public array $searchHits = []; /** * If spellcheck is on this field contains a collated query suggestion where in the appropriate diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php index 5d82d4fc12..22cf3be5f5 100644 --- a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -164,6 +164,8 @@ public function getMaxScore(): ?float /** * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ abstract protected function executeQuery( SearchService $searchService, From d81b0caed25eb8ca955fd2f8e35870bcccdf5f7a Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:42:09 +0100 Subject: [PATCH 05/27] [Pagerfanta] Aligned totalCount strict type hints (`int<0, max>`) --- src/contracts/Repository/Values/Content/ContentList.php | 4 +++- src/contracts/Repository/Values/Content/LocationList.php | 7 +++++-- src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php | 2 +- src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/contracts/Repository/Values/Content/ContentList.php b/src/contracts/Repository/Values/Content/ContentList.php index 4256dd114d..f15c73fd67 100644 --- a/src/contracts/Repository/Values/Content/ContentList.php +++ b/src/contracts/Repository/Values/Content/ContentList.php @@ -17,7 +17,7 @@ */ final class ContentList implements IteratorAggregate, TotalCountAwareInterface { - /** @var int */ + /** @phpstan-var int<0, max> */ private int $totalCount; /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ @@ -26,6 +26,8 @@ final class ContentList implements IteratorAggregate, TotalCountAwareInterface /** * @internal for internal use by Repository * + * @phpstan-param int<0, max> $totalCount + * * @param array<\Ibexa\Contracts\Core\Repository\Values\Content\Content> $contentItems */ public function __construct(int $totalCount, array $contentItems) diff --git a/src/contracts/Repository/Values/Content/LocationList.php b/src/contracts/Repository/Values/Content/LocationList.php index 02b5081768..f32d876352 100644 --- a/src/contracts/Repository/Values/Content/LocationList.php +++ b/src/contracts/Repository/Values/Content/LocationList.php @@ -18,7 +18,7 @@ * This class represents a queried location list holding a totalCount and a partial list of locations * (by offset/limit parameters and permission filters). * - * @property-read int $totalCount - the total count of found locations (filtered by permissions) + * @property-read int<0, max> $totalCount - the total count of found locations (filtered by permissions) * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locations - the partial list of * Locations controlled by offset/limit. **/ @@ -29,7 +29,7 @@ class LocationList extends ValueObject implements IteratorAggregate, TotalCountA * * Use {@see getTotalCount} to fetch it. * - * @var int + * @phpstan-var int<0, max> */ protected $totalCount = 0; @@ -48,6 +48,9 @@ public function getIterator(): Traversable return new ArrayIterator($this->locations); } + /** + * @phpstan-return int<0, max> + */ public function getTotalCount(): int { return $this->totalCount; diff --git a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php index 2a25ae0b9c..6697c055e9 100644 --- a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php @@ -29,7 +29,7 @@ final class ContentFilteringAdapter implements AdapterInterface private ?array $languageFilter; /** @phpstan-var int<0, max>|null */ - private ?int $totalCount; + private ?int $totalCount = null; /** * @param array|null $languageFilter diff --git a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php index ef4a0cea3e..0a59b0762f 100644 --- a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php @@ -23,8 +23,8 @@ final class LocationFilteringAdapter implements AdapterInterface /** @var array|null */ private $languageFilter; - /** @var int|null */ - private $totalCount; + /** @phpstan-var int<0, max>|null */ + private ?int $totalCount = null; public function __construct( LocationService $locationService, From 0c007adb0341c599ea375c6e056c7c8d50d26598 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:43:00 +0100 Subject: [PATCH 06/27] [Pagerfanta] Defined ContentViewQueryTypeMapper::map strict return type --- src/lib/QueryType/ContentViewQueryTypeMapper.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib/QueryType/ContentViewQueryTypeMapper.php b/src/lib/QueryType/ContentViewQueryTypeMapper.php index c9a37b39c5..e88852907a 100644 --- a/src/lib/QueryType/ContentViewQueryTypeMapper.php +++ b/src/lib/QueryType/ContentViewQueryTypeMapper.php @@ -7,6 +7,7 @@ namespace Ibexa\Core\QueryType; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Core\MVC\Symfony\View\ContentView; /** @@ -14,10 +15,5 @@ */ interface ContentViewQueryTypeMapper { - /** - * @param \Ibexa\Core\MVC\Symfony\View\ContentView $contentView - * - * @return \Ibexa\Core\QueryType\QueryType - */ - public function map(ContentView $contentView); + public function map(ContentView $contentView): Query; } From 484ca9f0ecdd8f3ab3c8e367c3c09ed37a3e0e2b Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:47:15 +0100 Subject: [PATCH 07/27] [Pagerfanta][PHPStan] Defined TFilteringLanguageFilter for Filtering API --- src/contracts/Repository/ContentService.php | 6 ++++-- src/contracts/Repository/LocationService.php | 6 ++++-- .../Pagination/Pagerfanta/ContentFilteringAdapter.php | 4 ++-- .../Pagination/Pagerfanta/LocationFilteringAdapter.php | 9 +++++++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index 9b744b855c..dea1594193 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -28,6 +28,8 @@ /** * This class provides service methods for managing content. + * + * @phpstan-type TFilteringLanguageFilter array */ interface ContentService { @@ -511,7 +513,7 @@ public function validate(ValueObject $object, array $context, ?array $fieldIdent /** * Fetches Content items from the Repository filtered by the given conditions. * - * @param array $languages A list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|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. */ @@ -522,7 +524,7 @@ public function find(Filter $filter, ?array $languages = null): ContentList; * * Counts total number of items returned by {@see ContentService::find()} with the same parameters. * - * @param array $languages A list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|null $languages $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. * diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php index 392306a00f..7f1f43615b 100644 --- a/src/contracts/Repository/LocationService.php +++ b/src/contracts/Repository/LocationService.php @@ -18,6 +18,8 @@ /** * Location service, used for complex subtree operations. + * + * @phpstan-type TFilteringLanguageFilter array */ interface LocationService { @@ -260,7 +262,7 @@ public function loadAllLocations(int $offset = 0, int $limit = 25): array; /** * Fetch a LocationList from the Repository filtered by the given conditions. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|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. */ @@ -269,7 +271,7 @@ public function find(Filter $filter, ?array $languages = null): LocationList; /** * Count total number of items returned by {@see find} method. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @phpstan-param TFilteringLanguageFilter|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. */ diff --git a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php index 6697c055e9..739af7b21a 100644 --- a/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php @@ -17,7 +17,7 @@ * * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> * - * @phpstan-type TFilteringLanguageFilter array|null + * @phpstan-import-type TFilteringLanguageFilter from \Ibexa\Contracts\Core\Repository\ContentService */ final class ContentFilteringAdapter implements AdapterInterface { @@ -25,7 +25,7 @@ final class ContentFilteringAdapter implements AdapterInterface private Filter $filter; - /** @var TFilteringLanguageFilter */ + /** @var TFilteringLanguageFilter|null */ private ?array $languageFilter; /** @phpstan-var int<0, max>|null */ diff --git a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php index 0a59b0762f..63da099074 100644 --- a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php @@ -12,6 +12,11 @@ use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; use Pagerfanta\Adapter\AdapterInterface; +/** + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @phpstan-import-type TFilteringLanguageFilter from \Ibexa\Contracts\Core\Repository\LocationService + */ final class LocationFilteringAdapter implements AdapterInterface { /** @var \Ibexa\Contracts\Core\Repository\LocationService */ @@ -20,8 +25,8 @@ final class LocationFilteringAdapter implements AdapterInterface /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\Filter */ private $filter; - /** @var array|null */ - private $languageFilter; + /** @phpstan-var TFilteringLanguageFilter|null */ + private ?array $languageFilter; /** @phpstan-var int<0, max>|null */ private ?int $totalCount = null; From d9742fd4e7f914fa2f3b339b4aab7969c1a5e391 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:50:30 +0100 Subject: [PATCH 08/27] [Pagerfanta][PHPStan] Defined TSearchLanguageFilter for Search API --- src/contracts/Repository/SearchService.php | 31 ++++++++++++------- src/contracts/Search/Handler.php | 26 +++++----------- .../AbstractSearchResultAdapter.php | 2 +- .../SearchHitAdapterFactory.php | 14 +++++++++ .../Pagerfanta/ContentSearchAdapter.php | 2 +- .../Pagerfanta/LocationSearchAdapter.php | 2 +- src/lib/Search/Legacy/Content/Handler.php | 20 ++++++++++++ 7 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/contracts/Repository/SearchService.php b/src/contracts/Repository/SearchService.php index 6b4b501f92..1fdae4af36 100644 --- a/src/contracts/Repository/SearchService.php +++ b/src/contracts/Repository/SearchService.php @@ -15,7 +15,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; /** - * Search service. + * @phpstan-type TSearchLanguageFilter array{languages?: string[], useAlwaysAvailable?: bool} */ interface SearchService { @@ -119,11 +119,11 @@ interface SearchService * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. + * * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult @@ -137,7 +137,10 @@ public function findContent(Query $query, array $languageFilter = [], bool $filt * it can be more efficient for use cases where you don't need the full Content. Also including use cases * where content will be loaded by separate code, like an ESI based sub requests that takes content ID as input. * - * @since 0.5.4.5 eZ Publish 5.4 (ezpublish-kernel 5.4) + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid * @@ -158,10 +161,11 @@ public function findContentInfo(Query $query, array $languageFilter = [], bool $ * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if criterion is not valid * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. + * * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content @@ -178,7 +182,10 @@ public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, /** * Finds Locations for the given query. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * @phpstan-param TSearchLanguageFilter $languageFilter Configuration for specifying prioritized languages the query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. + * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. diff --git a/src/contracts/Search/Handler.php b/src/contracts/Search/Handler.php index dc3c184a44..9d508ed10d 100644 --- a/src/contracts/Search/Handler.php +++ b/src/contracts/Search/Handler.php @@ -17,6 +17,8 @@ /** * The Search handler retrieves sets of of Content objects, based on a * set of criteria. + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ interface Handler { @@ -25,11 +27,7 @@ interface Handler * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findContent} * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With ContentInfo as SearchHit->valueObject */ @@ -38,28 +36,18 @@ public function findContent(Query $query, array $languageFilter = []); /** * Performs a query for a single content object. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface $filter - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo - * - *@throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} */ public function findSingle(CriterionInterface $filter, array $languageFilter = []); /** * Finds locations for the given $query. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With Location as SearchHit->valueObject */ diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php index 22cf3be5f5..925ca0e3fb 100644 --- a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -16,7 +16,7 @@ use Pagerfanta\Adapter\AdapterInterface; /** - * @phpstan-type TSearchLanguageFilter array{languages?: string[], useAlwaysAvailable?: bool} + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService * * @template T of \Ibexa\Contracts\Core\Repository\Values\ValueObject * diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php index 97684cd854..3dad98abab 100644 --- a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php @@ -18,6 +18,8 @@ /** * @internal + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ final class SearchHitAdapterFactory implements SearchHitAdapterFactoryInterface { @@ -29,6 +31,11 @@ public function __construct(SearchService $searchService) $this->searchService = $searchService; } + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + */ public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface { if ($query instanceof LocationQuery) { @@ -38,6 +45,13 @@ public function createAdapter(Query $query, array $languageFilter = []): Adapter return new ContentSearchHitAdapter($query, $this->searchService, $languageFilter); } + /** + * @phpstan-return \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + * + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface { if ($query instanceof LocationQuery) { diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php index 05e9e214b0..d159a4e15d 100644 --- a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php @@ -19,7 +19,7 @@ * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> * - * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ class ContentSearchAdapter implements AdapterInterface, SearchResultAdapter { diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php index 755365c3b9..160c76e9fb 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php @@ -19,7 +19,7 @@ * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Location> * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> * - * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ class LocationSearchAdapter implements AdapterInterface, SearchResultAdapter { diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php index 20c8890f0d..0c7eca7f70 100644 --- a/src/lib/Search/Legacy/Content/Handler.php +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -46,6 +46,8 @@ * 4) Additionally we might need a post-query filtering step, which filters * content objects based on criteria, which could not be converted in to * database statements. + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ class Handler implements SearchHandlerInterface { @@ -393,4 +395,22 @@ public function supports(int $capabilityFlag): bool { return false; } + + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return TSearchLanguageFilter + */ + private function setLanguageFilterDefaults(array $languageFilter): array + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + return $languageFilter; + } } From 1dacbcc4d65e291d324c54c3ef8456beb7a35c7d Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:51:43 +0100 Subject: [PATCH 09/27] [Pagerfanta] Aligned Core Pagerfanta implementation with Pagerfanta v3 --- src/lib/Pagination/Pagerfanta/Pagerfanta.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/lib/Pagination/Pagerfanta/Pagerfanta.php b/src/lib/Pagination/Pagerfanta/Pagerfanta.php index e3b5b9a5f2..ac84e931fb 100644 --- a/src/lib/Pagination/Pagerfanta/Pagerfanta.php +++ b/src/lib/Pagination/Pagerfanta/Pagerfanta.php @@ -11,30 +11,38 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Pagerfanta\Pagerfanta as BasePagerfanta; +/** + * @template TSearchResultAdapter + * + * @extends \Pagerfanta\Pagerfanta + */ final class Pagerfanta extends BasePagerfanta { - public function __construct(SearchResultAdapter $adapter) + /** + * @phpstan-param \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter $searchResultAdapter + */ + public function __construct(private readonly SearchResultAdapter $searchResultAdapter) { - parent::__construct($adapter); + parent::__construct($this->searchResultAdapter); } public function getAggregations(): AggregationResultCollection { - return $this->getAdapter()->getAggregations(); + return $this->searchResultAdapter->getAggregations(); } public function getTime(): ?float { - return $this->getAdapter()->getTime(); + return $this->searchResultAdapter->getTime(); } public function getTimedOut(): ?bool { - return $this->getAdapter()->getTimedOut(); + return $this->searchResultAdapter->getTimedOut(); } public function getMaxScore(): ?float { - return $this->getAdapter()->getMaxScore(); + return $this->searchResultAdapter->getMaxScore(); } } From e2897f44eb1a0e988134020db6b4c606774d626d Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:55:12 +0100 Subject: [PATCH 10/27] [Pagerfanta][PHPStan] Implemented Pagerfanta v3 templates --- .../BatchIteratorAdapter/AbstractSearchAdapter.php | 14 ++++++++++++-- .../ContentInfoSearchAdapter.php | 3 +++ .../BatchIteratorAdapter/ContentSearchAdapter.php | 3 +++ .../BatchIteratorAdapter/LocationSearchAdapter.php | 3 +++ src/contracts/Repository/SearchService.php | 6 ++++-- .../Repository/Values/Content/ContentList.php | 2 ++ src/contracts/Search/Handler.php | 4 ++-- .../Symfony/Controller/Content/QueryController.php | 8 +++----- .../Pagerfanta/FixedSearchResultHitAdapter.php | 12 ++++++++++-- 9 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php index 6d95b6ac63..b7e6677d0d 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php @@ -14,6 +14,11 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Iterator; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + */ abstract class AbstractSearchAdapter implements BatchIteratorAdapter { /** @var \Ibexa\Contracts\Core\Repository\SearchService */ @@ -22,8 +27,8 @@ abstract class AbstractSearchAdapter implements BatchIteratorAdapter /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query */ protected $query; - /** @var string[] */ - protected $languageFilter; + /** @phpstan-var TSearchLanguageFilter */ + protected array $languageFilter; /** @var bool */ protected $filterOnUserPermissions; @@ -49,5 +54,10 @@ final public function fetch(int $offset, int $limit): Iterator return $this->executeSearch($query)->getIterator(); } + /** + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ abstract protected function executeSearch(Query $query): SearchResult; } diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php index 8d7b4c318c..6d01445bbf 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php @@ -11,6 +11,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + */ final class ContentInfoSearchAdapter extends AbstractSearchAdapter { protected function executeSearch(Query $query): SearchResult diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php index 7216153afc..7e25c3ddd9 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php @@ -11,6 +11,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + */ final class ContentSearchAdapter extends AbstractSearchAdapter { protected function executeSearch(Query $query): SearchResult diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php index f5401e01c9..963fe0deda 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php @@ -13,6 +13,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @extends \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + */ final class LocationSearchAdapter extends AbstractSearchAdapter { public function __construct( diff --git a/src/contracts/Repository/SearchService.php b/src/contracts/Repository/SearchService.php index 1fdae4af36..6f81d18a82 100644 --- a/src/contracts/Repository/SearchService.php +++ b/src/contracts/Repository/SearchService.php @@ -126,7 +126,7 @@ interface SearchService * * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; @@ -193,7 +193,9 @@ public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, * useAlwaysAvailable defaults to true to avoid exceptions on missing translations * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid */ public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; diff --git a/src/contracts/Repository/Values/Content/ContentList.php b/src/contracts/Repository/Values/Content/ContentList.php index f15c73fd67..9fd7b03792 100644 --- a/src/contracts/Repository/Values/Content/ContentList.php +++ b/src/contracts/Repository/Values/Content/ContentList.php @@ -14,6 +14,8 @@ /** * A filtered Content items list iterator. + * + * @implements \IteratorAggregate<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ final class ContentList implements IteratorAggregate, TotalCountAwareInterface { diff --git a/src/contracts/Search/Handler.php b/src/contracts/Search/Handler.php index 9d508ed10d..970301be05 100644 --- a/src/contracts/Search/Handler.php +++ b/src/contracts/Search/Handler.php @@ -29,7 +29,7 @@ interface Handler * * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findContent} * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With ContentInfo as SearchHit->valueObject + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> */ public function findContent(Query $query, array $languageFilter = []); @@ -49,7 +49,7 @@ public function findSingle(CriterionInterface $filter, array $languageFilter = [ * * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With Location as SearchHit->valueObject + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> */ public function findLocations(LocationQuery $query, array $languageFilter = []); diff --git a/src/lib/MVC/Symfony/Controller/Content/QueryController.php b/src/lib/MVC/Symfony/Controller/Content/QueryController.php index 4ee23943cd..35fdafd6bb 100644 --- a/src/lib/MVC/Symfony/Controller/Content/QueryController.php +++ b/src/lib/MVC/Symfony/Controller/Content/QueryController.php @@ -14,8 +14,8 @@ use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use Ibexa\Core\QueryType\ContentViewQueryTypeMapper; -use Pagerfanta\Adapter\AdapterInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -132,11 +132,9 @@ private function runPagingQuery(ContentView $view, Request $request) } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * - * @return \Pagerfanta\Adapter\AdapterInterface + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> */ - private function getAdapter(Query $query): AdapterInterface + private function getAdapter(Query $query): SearchResultAdapter { if ($query instanceof LocationQuery) { return new LocationSearchHitAdapter($query, $this->searchService); diff --git a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php index 343f463b11..fdec3c289f 100644 --- a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php @@ -11,11 +11,19 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @implements \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit> + */ final class FixedSearchResultHitAdapter implements SearchResultAdapter { - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ - private $searchResult; + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ + private SearchResult $searchResult; + /** + * @phpstan-param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult + */ public function __construct(SearchResult $searchResult) { $this->searchResult = $searchResult; From 0647976a3a9ab16f0c84919426b7424bec5d90f0 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:56:48 +0100 Subject: [PATCH 11/27] [Pagerfanta] Fixed related classes' code quality and PHPDoc --- .../AbstractSearchAdapter.php | 11 ++++--- .../ContentInfoSearchAdapter.php | 3 ++ .../LocationSearchAdapter.php | 12 +++++++ src/contracts/Repository/SearchService.php | 31 ++----------------- src/contracts/Search/Handler.php | 9 +++--- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php index b7e6677d0d..ff0ea38aa2 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php @@ -21,18 +21,19 @@ */ abstract class AbstractSearchAdapter implements BatchIteratorAdapter { - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - protected $searchService; + protected SearchService $searchService; - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query */ - protected $query; + protected Query $query; /** @phpstan-var TSearchLanguageFilter */ protected array $languageFilter; /** @var bool */ - protected $filterOnUserPermissions; + protected bool $filterOnUserPermissions; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + */ public function __construct( SearchService $searchService, Query $query, diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php index 6d01445bbf..d19be293e4 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php @@ -16,6 +16,9 @@ */ final class ContentInfoSearchAdapter extends AbstractSearchAdapter { + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ protected function executeSearch(Query $query): SearchResult { return $this->searchService->findContentInfo( diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php index 963fe0deda..263675e277 100644 --- a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php @@ -8,6 +8,7 @@ namespace Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; use Ibexa\Contracts\Core\Repository\Values\Content\Query; @@ -29,6 +30,17 @@ public function __construct( protected function executeSearch(Query $query): SearchResult { + if (!$query instanceof LocationQuery) { + throw new InvalidArgumentException( + '$query', + sprintf( + 'Expected an instance of %s, got %s', + LocationQuery::class, + get_class($query) + ) + ); + } + return $this->searchService->findLocations( $query, $this->languageFilter, diff --git a/src/contracts/Repository/SearchService.php b/src/contracts/Repository/SearchService.php index 6f81d18a82..0d8a77dfe6 100644 --- a/src/contracts/Repository/SearchService.php +++ b/src/contracts/Repository/SearchService.php @@ -25,8 +25,6 @@ interface SearchService * Scoring, a search feature telling you how well one search hit scores compared to other items in the search result. * When this is supported you can expect search engine to populate SearchHit->score and SearchResult->maxScore * properties as well as sort by this if no sort clauses are specified. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SCORING = 1; @@ -39,8 +37,6 @@ interface SearchService * - It might not support all facets, by design it will only return facets for facet builders the search engine supports. * - Some of the faceting features are still work in progress in API and won't be further matured before in 7 .x * releases - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_FACETS = 2; @@ -51,8 +47,6 @@ interface SearchService * generate custom fields, like a different representation (format, ...) of an existing field or similar. And 2. * allow you on some search criteria to specify this custom field to rather query on that instead of the default * field generated by the system. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_CUSTOM_FIELDS = 4; @@ -64,8 +58,6 @@ interface SearchService * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should * interact with FullText criterion (singular) which is the most relevant here. Also given how FullText can be part of a more complex criteria it * might imply a need to more strictly define where users are supposed to place FullText vs other criteria. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SPELLCHECK = 8; @@ -82,8 +74,6 @@ interface SearchService * @internal Maybe it should rather give just matched text and hint of which field (several: one with best score) * was matched and leave it to field type to render result with that info taking into account. But for now it is * designed as simple string field, so should be string with for instance `` around matched text. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_HIGHLIGHT = 16; @@ -92,8 +82,6 @@ interface SearchService * * WARNING: This feature is considered experimental given it is not completely clear what it is supposed to do. Feature * might be deprecated in the future. - * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) */ public const CAPABILITY_SUGGEST = 32; @@ -102,7 +90,6 @@ interface SearchService * * Advance full text is a feature making to possible by current engine to parse advance full text expressions. * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\FullText */ public const CAPABILITY_ADVANCED_FULLTEXT = 64; @@ -142,15 +129,11 @@ public function findContent(Query $query, array $languageFilter = [], bool $filt * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid */ public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; @@ -167,8 +150,6 @@ public function findContentInfo(Query $query, array $languageFilter = [], bool $ * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ public function findSingle(CriterionInterface $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content; @@ -187,10 +168,6 @@ public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, * Currently, it supports: ['languages' => [, ...], 'useAlwaysAvailable' => bool]. * `useAlwaysAvailable` defaults to true to avoid exceptions on missing translations. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> @@ -204,8 +181,6 @@ public function findLocations(LocationQuery $query, array $languageFilter = [], * * Will return false if search engine does not implement {@see \Ibexa\Contracts\Core\Search\Capable}. * - * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) - * * @param int $capabilityFlag One of CAPABILITY_* constants. * * @return bool diff --git a/src/contracts/Search/Handler.php b/src/contracts/Search/Handler.php index 970301be05..20ec7388bf 100644 --- a/src/contracts/Search/Handler.php +++ b/src/contracts/Search/Handler.php @@ -13,6 +13,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; /** * The Search handler retrieves sets of of Content objects, based on a @@ -31,18 +32,18 @@ interface Handler * * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> */ - public function findContent(Query $query, array $languageFilter = []); + public function findContent(Query $query, array $languageFilter = []): SearchResult; /** * Performs a query for a single content object. * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criteria * * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} */ - public function findSingle(CriterionInterface $filter, array $languageFilter = []); + public function findSingle(CriterionInterface $filter, array $languageFilter = []): Content\ContentInfo; /** * Finds locations for the given $query. @@ -51,7 +52,7 @@ public function findSingle(CriterionInterface $filter, array $languageFilter = [ * * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> */ - public function findLocations(LocationQuery $query, array $languageFilter = []); + public function findLocations(LocationQuery $query, array $languageFilter = []): SearchResult; /** * Suggests a list of values for the given prefix. From ddf6dbe5434270866219d1a575b4347842ec6e3e Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 14:58:33 +0100 Subject: [PATCH 12/27] [Pagerfanta] Fixed related implementation code quality --- .../AbstractSearchResultAdapter.php | 2 +- .../FixedSearchResultHitAdapter.php | 2 +- .../Pagerfanta/LocationFilteringAdapter.php | 5 +- .../Pagerfanta/LocationSearchHitAdapter.php | 4 +- ...eryParameterContentViewQueryTypeMapper.php | 20 +++--- src/lib/Repository/SearchService.php | 13 ---- src/lib/Search/Legacy/Content/Handler.php | 62 +++---------------- 7 files changed, 23 insertions(+), 85 deletions(-) diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php index 925ca0e3fb..19aef03e1c 100644 --- a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -32,7 +32,7 @@ abstract class AbstractSearchResultAdapter implements AdapterInterface, SearchRe /** @phpstan-var TSearchLanguageFilter */ private array $languageFilter; - private ?AggregationResultCollection $aggregations; + private ?AggregationResultCollection $aggregations = null; private ?float $time; diff --git a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php index fdec3c289f..34df32a609 100644 --- a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php @@ -34,7 +34,7 @@ public function getNbResults(): int return $this->searchResult->totalCount ?? -1; } - public function getSlice($offset, $length) + public function getSlice(int $offset, int $length): iterable { return $this->searchResult->searchHits; } diff --git a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php index 63da099074..dc3bc9fc6e 100644 --- a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php @@ -19,11 +19,10 @@ */ final class LocationFilteringAdapter implements AdapterInterface { - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; + private LocationService $locationService; /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\Filter */ - private $filter; + private Filter $filter; /** @phpstan-var TFilteringLanguageFilter|null */ private ?array $languageFilter; diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php index 0e4a5a00f6..fad3f40a59 100644 --- a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php +++ b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php @@ -26,12 +26,12 @@ public function __construct(LocationQuery $query, SearchService $searchService, } /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function executeQuery(SearchService $searchService, Query $query, array $languageFilter): SearchResult { + assert($query instanceof LocationQuery); + return $searchService->findLocations($query, $languageFilter); } } diff --git a/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php index 9023d58770..676bf942b0 100644 --- a/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php +++ b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php @@ -7,8 +7,9 @@ namespace Ibexa\Core\QueryType; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Core\MVC\Symfony\View\ContentView; -use InvalidArgumentException; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; /** @@ -16,28 +17,27 @@ */ class QueryParameterContentViewQueryTypeMapper implements ContentViewQueryTypeMapper { - /** @var QueryTypeRegistry */ - private $queryTypeRegistry; + private QueryTypeRegistry $queryTypeRegistry; public function __construct(QueryTypeRegistry $queryTypeRegistry) { $this->queryTypeRegistry = $queryTypeRegistry; } - public function map(ContentView $contentView) + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function map(ContentView $contentView): Query { - if (!$contentView instanceof ContentView) { - throw new InvalidArgumentException('ContentView expected'); - } - if (!$contentView->hasParameter('query')) { throw new InvalidArgumentException('query', "Required 'query' view parameter is missing"); } $queryOptions = $contentView->getParameter('query'); - $queryType = $this->queryTypeRegistry->getQueryType($queryOptions['query_type']); - return $queryType->getQuery($this->extractParametersFromContentView($contentView)); + return $this->queryTypeRegistry + ->getQueryType($queryOptions['query_type']) + ->getQuery($this->extractParametersFromContentView($contentView)); } /** diff --git a/src/lib/Repository/SearchService.php b/src/lib/Repository/SearchService.php index 76555a3929..625ca37c82 100644 --- a/src/lib/Repository/SearchService.php +++ b/src/lib/Repository/SearchService.php @@ -81,19 +81,6 @@ public function __construct( $this->backgroundIndexer = $backgroundIndexer; } - /** - * Finds content objects for the given query. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult - */ public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult { $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php index 0c7eca7f70..7afb917ad5 100644 --- a/src/lib/Search/Legacy/Content/Handler.php +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -118,28 +118,9 @@ public function __construct( $this->mapper = $mapper; } - /** - * Finds content objects for the given query. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult - */ - public function findContent(Query $query, array $languageFilter = []) + public function findContent(Query $query, array $languageFilter = []): SearchResult { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $start = microtime(true); $query->filter = $query->filter ?: new Criterion\MatchAll(); @@ -201,30 +182,9 @@ protected function extractMatchedLanguage($languageMask, $mainLanguageId, $langu return null; } - /** - * Performs a query for a single content object. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface $filter - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions - */ - public function findSingle(CriterionInterface $filter, array $languageFilter = []) + public function findSingle(CriterionInterface $filter, array $languageFilter = []): Content\ContentInfo { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $searchQuery = new Query(); $searchQuery->filter = $filter; @@ -241,20 +201,12 @@ public function findSingle(CriterionInterface $filter, array $languageFilter = [ throw new InvalidArgumentException('totalCount', 'findSingle() found more then one item for the given $criterion'); } - $first = reset($result->searchHits); - - return $first->valueObject; + return reset($result->searchHits)->valueObject; } - public function findLocations(LocationQuery $query, array $languageFilter = []) + public function findLocations(LocationQuery $query, array $languageFilter = []): SearchResult { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } + $languageFilter = $this->setLanguageFilterDefaults($languageFilter); $start = microtime(true); $query->filter = $query->filter ?: new Criterion\MatchAll(); From 2641c0564b2ca719b0c2619d450ba97a64a2a225 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 15:00:25 +0100 Subject: [PATCH 13/27] [Pagerfanta][Tests] Refactored adapter tests to follow the changes --- ...BaseContentSearchResultAdapterTestCase.php | 131 +++++++++++++++ ...aseLocationSearchResultAdapterTestCase.php | 139 ++++++++++++++++ .../BaseSearchResultAdapterTestCase.php | 61 +++++++ .../Pagination/ContentSearchAdapterTest.php | 70 +++++--- .../ContentSearchHitAdapterTest.php | 148 +++-------------- .../Pagination/LocationSearchAdapterTest.php | 72 +++++--- .../LocationSearchHitAdapterTest.php | 155 +++--------------- tests/lib/Pagination/PagerfantaTest.php | 16 +- .../AbstractSearchAdapterTest.php | 10 ++ .../ContentInfoSearchAdapterTest.php | 3 + .../ContentSearchAdapterTest.php | 5 + .../LocationSearchAdapterTest.php | 5 + 12 files changed, 507 insertions(+), 308 deletions(-) create mode 100644 tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php create mode 100644 tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php create mode 100644 tests/lib/Pagination/BaseSearchResultAdapterTestCase.php diff --git a/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php new file mode 100644 index 0000000000..de4bab0c03 --- /dev/null +++ b/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php @@ -0,0 +1,131 @@ + + */ +abstract class BaseContentSearchResultAdapterTestCase extends BaseSearchResultAdapterTestCase +{ + protected function mockQueryForGetNbResults(int $nbResults): Query + { + $query = $this->createTestQuery(); + + // Count query will necessarily have a 0 limit and empty aggregations/facet builders. + $countQuery = clone $query; + $countQuery->limit = 0; + $countQuery->aggregations = []; + + $searchResult = new SearchResult(['totalCount' => $nbResults]); + $this->searchService + ->expects(self::once()) + ->method('findContent') + ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + /** + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\ValueObject>> + */ + protected function mockSearchHitsForGetSlice( + Query $query, + int $nbResults, + AggregationResultCollection $aggregationsResults + ): array { + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $searchQuery = clone $query; + $searchQuery->offset = self::EXAMPLE_OFFSET; + $searchQuery->limit = self::EXAMPLE_LIMIT; + $searchQuery->performCount = false; + + $hits = []; + for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { + $hits[] = new SearchHit([ + 'valueObject' => $this->createMock(APIContent::class), + ]); + } + + $searchResult = new SearchResult( + [ + 'searchHits' => $hits, + 'totalCount' => $nbResults, + 'aggregations' => $aggregationsResults, + 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, + 'timedOut' => true, + 'time' => self::EXAMPLE_RESULT_TIME, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findContent') + ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $hits; + } + + protected function mockQueryForGetAggregations(AggregationResultCollection $exceptedAggregationsResults): Query + { + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $aggregationQuery = clone $query; + $aggregationQuery->offset = 0; + $aggregationQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'searchHits' => [], + 'totalCount' => 0, + 'aggregations' => $exceptedAggregationsResults, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findContent') + ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + protected function createTestQuery(int $limit = 25, int $offset = 0): Query + { + $query = new Query(); + $query->query = $this->createMock(CriterionInterface::class); + $query->aggregations[] = $this->createMock(Aggregation::class); + $query->sortClauses[] = $this->createMock(SortClause::class); + $query->offset = $offset; + $query->limit = $limit; + + return $query; + } +} diff --git a/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php new file mode 100644 index 0000000000..3453dc8791 --- /dev/null +++ b/tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php @@ -0,0 +1,139 @@ + + */ +abstract class BaseLocationSearchResultAdapterTestCase extends BaseSearchResultAdapterTestCase +{ + protected function mockQueryForGetNbResults(int $nbResults): LocationQuery + { + $query = $this->createTestQuery(); + + // Count query will necessarily have a 0 limit. + $countQuery = clone $query; + $countQuery->aggregations = []; + $countQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'totalCount' => $nbResults, + ] + ); + + $this->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + /** + * @phpstan-return list<\Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Repository\Values\ValueObject>> + */ + protected function mockSearchHitsForGetSlice( + LocationQuery $query, + int $nbResults, + AggregationResultCollection $aggregationsResults + ): array { + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $searchQuery = clone $query; + $searchQuery->offset = self::EXAMPLE_OFFSET; + $searchQuery->limit = self::EXAMPLE_LIMIT; + $searchQuery->performCount = false; + + $hits = []; + for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { + $hits[] = new SearchHit( + [ + 'valueObject' => $this->createMock(APILocation::class), + ] + ); + } + + $searchResult = new SearchResult( + [ + 'searchHits' => $hits, + 'totalCount' => $nbResults, + 'aggregations' => $aggregationsResults, + 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, + 'timedOut' => true, + 'time' => self::EXAMPLE_RESULT_TIME, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $hits; + } + + protected function mockQueryForGetAggregations( + AggregationResultCollection $expectedAggregationsResults + ): LocationQuery { + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + + // Injected query is being cloned to modify offset/limit, + // so we need to do the same here for our assertions. + $aggregationQuery = clone $query; + $aggregationQuery->offset = 0; + $aggregationQuery->limit = 0; + + $searchResult = new SearchResult( + [ + 'searchHits' => [], + 'totalCount' => 0, + 'aggregations' => $expectedAggregationsResults, + ] + ); + + $this + ->searchService + ->expects(self::once()) + ->method('findLocations') + ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult) + ; + + return $query; + } + + protected function createTestQuery(int $limit = 25, int $offset = 0): LocationQuery + { + $query = new LocationQuery(); + $query->query = $this->createMock(CriterionInterface::class); + $query->aggregations[] = $this->createMock(Aggregation::class); + $query->sortClauses[] = $this->createMock(SortClause::class); + $query->offset = $offset; + $query->limit = $limit; + + return $query; + } +} diff --git a/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php new file mode 100644 index 0000000000..cb98cd5c1e --- /dev/null +++ b/tests/lib/Pagination/BaseSearchResultAdapterTestCase.php @@ -0,0 +1,61 @@ + ['eng-GB', 'pol-PL'], + 'useAlwaysAvailable' => true, + ]; + + protected const float EXAMPLE_RESULT_MAX_SCORE = 5.123; + protected const float EXAMPLE_RESULT_TIME = 30.0; + + protected SearchService & MockObject $searchService; + + protected function setUp(): void + { + parent::setUp(); + $this->searchService = $this->createMock(SearchService::class); + } + + abstract public function testGetNbResults(): void; + + abstract public function testGetSlice(): void; + + abstract public function testGetAggregations(): void; + + /** + * @phpstan-param TSearchResultAdapter $adapter + */ + protected function assertSearchResult( + int $nbResults, + SearchResultAdapter $adapter, + AggregationResultCollection $aggregationsResults + ): void { + self::assertSame($nbResults, $adapter->getNbResults()); + self::assertSame($aggregationsResults, $adapter->getAggregations()); + self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); + self::assertTrue($adapter->getTimedOut()); + self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + } +} diff --git a/tests/lib/Pagination/ContentSearchAdapterTest.php b/tests/lib/Pagination/ContentSearchAdapterTest.php index ecd6475fb3..9ff0b6d8a4 100644 --- a/tests/lib/Pagination/ContentSearchAdapterTest.php +++ b/tests/lib/Pagination/ContentSearchAdapterTest.php @@ -4,43 +4,71 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter; -class ContentSearchAdapterTest extends ContentSearchHitAdapterTest +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseContentSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter> + */ +final class ContentSearchAdapterTest extends BaseContentSearchResultAdapterTestCase { /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []): ContentSearchAdapter { return new ContentSearchAdapter($query, $searchService, $languageFilter); } - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) + public function testGetNbResults(): void { - $expectedResult = []; + $nbResults = 123; + + $query = $this->mockQueryForGetNbResults($nbResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame($nbResults, $adapter->getNbResults()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($nbResults, $adapter->getNbResults()); + } + + public function testGetSlice(): void + { + $aggregationsResults = new AggregationResultCollection(); + $nbResults = 123; + + $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + array_map(static fn (SearchHit $hit) => $hit->valueObject, $hits), + $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) + ); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); + } + + public function testGetAggregations(): void + { + $exceptedAggregationsResults = new AggregationResultCollection(); + + $query = $this->mockQueryForGetAggregations($exceptedAggregationsResults); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - return $expectedResult; + self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/ContentSearchHitAdapterTest.php b/tests/lib/Pagination/ContentSearchHitAdapterTest.php index 9abb45c145..8e549a9acf 100644 --- a/tests/lib/Pagination/ContentSearchHitAdapterTest.php +++ b/tests/lib/Pagination/ContentSearchHitAdapterTest.php @@ -4,35 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; -use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; -use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; -class ContentSearchHitAdapterTest extends TestCase +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseContentSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter> + */ +final class ContentSearchHitAdapterTest extends BaseContentSearchResultAdapterTestCase { - private const EXAMPLE_LIMIT = 40; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - private const EXAMPLE_RESULT_MAX_SCORE = 5.123; - private const EXAMPLE_RESULT_TIME = 30.0; - - /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ - protected $searchService; + protected SearchService & MockObject $searchService; protected function setUp(): void { @@ -41,36 +30,21 @@ protected function setUp(): void } /** - * Returns the adapter to test. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) - { + protected function getAdapter( + Query $query, + SearchService $searchService, + array $languageFilter = [] + ): ContentSearchHitAdapter { return new ContentSearchHitAdapter($query, $searchService, $languageFilter); } - public function testGetNbResults() + public function testGetNbResults(): void { $nbResults = 123; - $query = $this->createTestQuery(); - - // Count query will necessarily have a 0 limit and empty aggregations/facet builders. - $countQuery = clone $query; - $countQuery->limit = 0; - $countQuery->aggregations = []; - - $searchResult = new SearchResult(['totalCount' => $nbResults]); - $this->searchService - ->expects(self::once()) - ->method('findContent') - ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetNbResults($nbResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -79,80 +53,28 @@ public function testGetNbResults() self::assertSame($nbResults, $adapter->getNbResults()); } - public function testGetSlice() + public function testGetSlice(): void { - $nbResults = 123; $aggregationsResults = new AggregationResultCollection(); + $nbResults = 123; $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $searchQuery = clone $query; - $searchQuery->offset = self::EXAMPLE_OFFSET; - $searchQuery->limit = self::EXAMPLE_LIMIT; - $searchQuery->performCount = false; - - $hits = []; - for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { - $hits[] = new SearchHit([ - 'valueObject' => $this->createMock(APIContent::class), - ]); - } - - $searchResult = new SearchResult([ - 'searchHits' => $hits, - 'totalCount' => $nbResults, - 'aggregations' => $aggregationsResults, - 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, - 'timedOut' => true, - 'time' => self::EXAMPLE_RESULT_TIME, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findContent') - ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); self::assertSame( - $this->getExpectedFinalResultFromHits($hits), + $hits, $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) ); - self::assertSame($nbResults, $adapter->getNbResults()); - self::assertSame($aggregationsResults, $adapter->getAggregations()); - self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); - self::assertTrue($adapter->getTimedOut()); - self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); } public function testGetAggregations(): void { $exceptedAggregationsResults = new AggregationResultCollection(); - $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $aggregationQuery = clone $query; - $aggregationQuery->offset = 0; - $aggregationQuery->limit = 0; - - $searchResult = new SearchResult([ - 'searchHits' => [], - 'totalCount' => 0, - 'aggregations' => $exceptedAggregationsResults, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findContent') - ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetAggregations($exceptedAggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -160,28 +82,4 @@ public function testGetAggregations(): void // Running a 2nd time to ensure SearchService::findContent() is called only once. self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - return $hits; - } - - private function createTestQuery(int $limit = 25, int $offset = 0): Query - { - $query = new Query(); - $query->query = $this->createMock(CriterionInterface::class); - $query->aggregations[] = $this->createMock(Aggregation::class); - $query->sortClauses[] = $this->createMock(SortClause::class); - $query->offset = $offset; - $query->limit = $limit; - - return $query; - } } diff --git a/tests/lib/Pagination/LocationSearchAdapterTest.php b/tests/lib/Pagination/LocationSearchAdapterTest.php index eea38eb32c..86cd980bd5 100644 --- a/tests/lib/Pagination/LocationSearchAdapterTest.php +++ b/tests/lib/Pagination/LocationSearchAdapterTest.php @@ -4,42 +4,74 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter; -class LocationSearchAdapterTest extends LocationSearchHitAdapterTest +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseLocationSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter> + */ +final class LocationSearchAdapterTest extends BaseLocationSearchResultAdapterTestCase { /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * - * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []): LocationSearchAdapter { return new LocationSearchAdapter($query, $searchService, $languageFilter); } - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) + public function testGetNbResults(): void + { + $nbResults = 123; + $query = $this->mockQueryForGetNbResults($nbResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame($nbResults, $adapter->getNbResults()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($nbResults, $adapter->getNbResults()); + } + + public function testGetSlice(): void + { + $nbResults = 123; + $aggregationsResults = new AggregationResultCollection(); + $query = $this->createTestQuery(); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); + + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + array_map(static fn (SearchHit $hit) => $hit->valueObject, $hits), + $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) + ); + + self::assertSame($nbResults, $adapter->getNbResults()); + self::assertSame($aggregationsResults, $adapter->getAggregations()); + self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); + self::assertTrue($adapter->getTimedOut()); + self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + } + + public function testGetAggregations(): void { - $expectedResult = []; + $expectedAggregationsResults = new AggregationResultCollection(); + + $query = $this->mockQueryForGetAggregations($expectedAggregationsResults); - /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } + $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - return $expectedResult; + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); + // Running a 2nd time to ensure SearchService::findContent() is called only once. + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/LocationSearchHitAdapterTest.php b/tests/lib/Pagination/LocationSearchHitAdapterTest.php index 6298ec5b44..60d8eaa098 100644 --- a/tests/lib/Pagination/LocationSearchHitAdapterTest.php +++ b/tests/lib/Pagination/LocationSearchHitAdapterTest.php @@ -4,76 +4,36 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); namespace Ibexa\Tests\Core\Pagination; use Ibexa\Contracts\Core\Repository\SearchService; -use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; -use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; -use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; -use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use PHPUnit\Framework\TestCase; -class LocationSearchHitAdapterTest extends TestCase +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Pagination\BaseLocationSearchResultAdapterTestCase<\Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter> + */ +final class LocationSearchHitAdapterTest extends BaseLocationSearchResultAdapterTestCase { - private const EXAMPLE_LIMIT = 40; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - private const EXAMPLE_RESULT_MAX_SCORE = 5.123; - private const EXAMPLE_RESULT_TIME = 30.0; - - /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ - protected $searchService; - - protected function setUp(): void - { - parent::setUp(); - $this->searchService = $this->createMock(SearchService::class); - } - /** * Returns the adapter to test. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query - * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter + * @phpstan-param TSearchLanguageFilter $languageFilter */ - protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []): LocationSearchHitAdapter { return new LocationSearchHitAdapter($query, $searchService, $languageFilter); } - public function testGetNbResults() + public function testGetNbResults(): void { $nbResults = 123; - $query = $this->createTestQuery(); - - // Count query will necessarily have a 0 limit. - $countQuery = clone $query; - $countQuery->aggregations = []; - $countQuery->limit = 0; - - $searchResult = new SearchResult([ - 'totalCount' => $nbResults, - ]); - - $this->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($countQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetNbResults($nbResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); @@ -82,110 +42,33 @@ public function testGetNbResults() self::assertSame($nbResults, $adapter->getNbResults()); } - public function testGetSlice() + public function testGetSlice(): void { $nbResults = 123; $aggregationsResults = new AggregationResultCollection(); - $query = $this->createTestQuery(); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $searchQuery = clone $query; - $searchQuery->offset = self::EXAMPLE_OFFSET; - $searchQuery->limit = self::EXAMPLE_LIMIT; - $searchQuery->performCount = false; - - $hits = []; - for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { - $hits[] = new SearchHit([ - 'valueObject' => $this->createMock(APILocation::class), - ]); - } - - $searchResult = new SearchResult([ - 'searchHits' => $hits, - 'totalCount' => $nbResults, - 'aggregations' => $aggregationsResults, - 'maxScore' => self::EXAMPLE_RESULT_MAX_SCORE, - 'timedOut' => true, - 'time' => self::EXAMPLE_RESULT_TIME, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($searchQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $hits = $this->mockSearchHitsForGetSlice($query, $nbResults, $aggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); self::assertSame( - $this->getExpectedFinalResultFromHits($hits), + $hits, $adapter->getSlice(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT) ); - self::assertSame($nbResults, $adapter->getNbResults()); - self::assertSame($aggregationsResults, $adapter->getAggregations()); - self::assertSame(self::EXAMPLE_RESULT_MAX_SCORE, $adapter->getMaxScore()); - self::assertTrue($adapter->getTimedOut()); - self::assertSame(self::EXAMPLE_RESULT_TIME, $adapter->getTime()); + $this->assertSearchResult($nbResults, $adapter, $aggregationsResults); } public function testGetAggregations(): void { - $exceptedAggregationsResults = new AggregationResultCollection(); - - $query = $this->createTestQuery(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT); - - // Injected query is being cloned to modify offset/limit, - // so we need to do the same here for our assertions. - $aggregationQuery = clone $query; - $aggregationQuery->offset = 0; - $aggregationQuery->limit = 0; + $expectedAggregationsResults = new AggregationResultCollection(); - $searchResult = new SearchResult([ - 'searchHits' => [], - 'totalCount' => 0, - 'aggregations' => $exceptedAggregationsResults, - ]); - - $this - ->searchService - ->expects(self::once()) - ->method('findLocations') - ->with($aggregationQuery, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); + $query = $this->mockQueryForGetAggregations($expectedAggregationsResults); $adapter = $this->getAdapter($query, $this->searchService, self::EXAMPLE_LANGUAGE_FILTER); - self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); // Running a 2nd time to ensure SearchService::findContent() is called only once. - self::assertSame($exceptedAggregationsResults, $adapter->getAggregations()); - } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - return $hits; - } - - private function createTestQuery(int $limit = 25, int $offset = 0): LocationQuery - { - $query = new LocationQuery(); - $query->query = $this->createMock(CriterionInterface::class); - $query->aggregations[] = $this->createMock(Aggregation::class); - $query->sortClauses[] = $this->createMock(SortClause::class); - $query->offset = $offset; - $query->limit = $limit; - - return $query; + self::assertSame($expectedAggregationsResults, $adapter->getAggregations()); } } diff --git a/tests/lib/Pagination/PagerfantaTest.php b/tests/lib/Pagination/PagerfantaTest.php index 5611a851a8..9563fba5ae 100644 --- a/tests/lib/Pagination/PagerfantaTest.php +++ b/tests/lib/Pagination/PagerfantaTest.php @@ -11,18 +11,22 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +/** + * @template TSearchResultAdapter + */ final class PagerfantaTest extends TestCase { - private const EXAMPLE_TIME_RESULT = 30.0; - private const EXAMPLE_MAX_SCORE_RESULT = 5.12354; + private const float EXAMPLE_TIME_RESULT = 30.0; + private const float EXAMPLE_MAX_SCORE_RESULT = 5.12354; - /** @var \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter|\PHPUnit\Framework\MockObject\MockObject */ - private $adapter; + /** @phpstan-var \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter & \PHPUnit\Framework\MockObject\MockObject */ + private SearchResultAdapter & MockObject $adapter; - /** @var \Ibexa\Core\Pagination\Pagerfanta\Pagerfanta */ - private $pagerfanta; + /** @var \Ibexa\Core\Pagination\Pagerfanta\Pagerfanta */ + private Pagerfanta $pagerfanta; protected function setUp(): void { diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php index 2203aa8f35..14b5186ac5 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php @@ -16,6 +16,11 @@ use Iterator; use PHPUnit\Framework\TestCase; +/** + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + */ abstract class AbstractSearchAdapterTest extends TestCase { protected const EXAMPLE_LANGUAGE_FILTER = [ @@ -59,6 +64,11 @@ final public function testFetch(): void self::assertEquals(25, $originalQuery->limit); } + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter + */ abstract protected function createAdapterUnderTest( SearchService $searchService, Query $query, diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php index 2fa8ab7ad2..cf761fd26e 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php @@ -13,6 +13,9 @@ use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> + */ final class ContentInfoSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php index 63691ac19b..6319549771 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php @@ -13,6 +13,11 @@ use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService + * + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + */ final class ContentSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php index 9d330f4be7..8b083a1121 100644 --- a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php @@ -14,6 +14,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; use Ibexa\Contracts\Core\Repository\Values\Content\Query; +/** + * @extends \Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + */ final class LocationSearchAdapterTest extends AbstractSearchAdapterTest { protected function createAdapterUnderTest( @@ -22,6 +25,8 @@ protected function createAdapterUnderTest( array $languageFilter, bool $filterOnPermissions ): AbstractSearchAdapter { + self::assertInstanceOf(LocationQuery::class, $query); + return new LocationSearchAdapter( $searchService, $query, From 5041befe9dc1055d21b299b892cd0c4bdd588a53 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 15:01:31 +0100 Subject: [PATCH 14/27] [Pagerfanta][Tests] Improved code quality of related test classes --- ...ServiceTranslationLanguageFallbackTest.php | 15 ++++++++- .../Controller/QueryRenderControllerTest.php | 29 ++++++++++++---- .../Repository/Service/Mock/SearchTest.php | 33 +++++++------------ 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php index 8e15ed4e0c..09c200b469 100644 --- a/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php +++ b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php @@ -24,6 +24,10 @@ * @group integration * @group search * @group language_fallback + * + * @template TSearchHitValueObject + * + * @phpstan-type TIndexMap array{dedicated: string, shared: string, single: string, cloud: string} */ class SearchServiceTranslationLanguageFallbackTest extends BaseTest { @@ -1694,7 +1698,12 @@ protected function getSetupType() throw new RuntimeException("Backend cores setup '{$coresSetup}' is not handled"); } - protected function getIndexName($indexMap) + /** + * @phpstan-param TIndexMap $indexMap + * + * @throws \ErrorException + */ + protected function getIndexName(array $indexMap): ?string { $setupFactory = $this->getSetupFactory(); @@ -1882,6 +1891,10 @@ public function testFindLocationsMultiple( } } + /** + * @phpstan-param TIndexMap $indexMap + * @phpstan-param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $searchHit + */ private function assertIndexName(array $indexMap, SearchHit $searchHit): void { $indexName = $this->getIndexName($indexMap); diff --git a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php index 1c75dd39e1..c4ac98007d 100644 --- a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php +++ b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php @@ -15,23 +15,27 @@ use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use Ibexa\Core\Query\QueryFactoryInterface; -use Pagerfanta\Adapter\AdapterInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +/** + * @phpstan-type TOptionsArray array + */ final class QueryRenderControllerTest extends TestCase { - private const EXAMPLE_CURRENT_PAGE = 3; - private const EXAMPLE_MAX_PER_PAGE = 100; + private const int EXAMPLE_CURRENT_PAGE = 3; + private const int EXAMPLE_MAX_PER_PAGE = 100; - private const MIN_OPTIONS = [ + /** @phpstan-var TOptionsArray */ + private const array MIN_OPTIONS = [ 'query' => [ 'query_type' => 'ExampleQuery', ], 'template' => 'example.html.twig', ]; - private const ALL_OPTIONS = [ + /** @phpstan-var TOptionsArray */ + private const array ALL_OPTIONS = [ 'query' => [ 'query_type' => 'ExampleQuery', 'parameters' => [ @@ -84,6 +88,9 @@ public function testRenderQueryWithMinOptions(): void ); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ public function testRenderQueryWithAllOptions(): void { $adapter = $this->configureMocks(self::ALL_OPTIONS); @@ -102,7 +109,14 @@ public function testRenderQueryWithAllOptions(): void ); } - private function configureMocks(array $options): AdapterInterface + /** + * @phpstan-param TOptionsArray $options + * + * @template TItem + * + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter + */ + private function configureMocks(array $options): SearchResultAdapter { $query = new Query(); @@ -124,6 +138,9 @@ private function configureMocks(array $options): AdapterInterface return $adapter; } + /** + * @phpstan-param TOptionsArray $options + */ private function assertRenderQueryResult( QueryView $expectedView, array $options, diff --git a/tests/lib/Repository/Service/Mock/SearchTest.php b/tests/lib/Repository/Service/Mock/SearchTest.php index 987cdbcda4..fdd14abe7b 100644 --- a/tests/lib/Repository/Service/Mock/SearchTest.php +++ b/tests/lib/Repository/Service/Mock/SearchTest.php @@ -245,7 +245,7 @@ public function testFindContentWhenDomainMapperThrowsException() ->with(self::equalTo($result), self::equalTo([])) ->willReturnCallback(static function (SearchResult $spiResult) use ($info) { unset($spiResult->searchHits[0]); - --$spiResult->totalCount; + $spiResult->totalCount = $spiResult->totalCount > 0 ? --$spiResult->totalCount : 0; return [$info]; }); @@ -583,16 +583,10 @@ public function testFindSingleThrowsHandlerException() $service->findSingle($criterionMock, [], true); } - /** - * Test for the findSingle() method. - * - * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion - * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle - */ - public function testFindSingle() + public function testFindSingle(): void { $repositoryMock = $this->getRepositoryMock(); - /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler&\PHPUnit\Framework\MockObject\MockObject $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -608,13 +602,11 @@ public function testFindSingle() $repositoryMock ->expects(self::once()) ->method('getContentService') - ->will( - self::returnValue( - $contentServiceMock = $this - ->getMockBuilder(ContentService::class) - ->disableOriginalConstructor() - ->getMock() - ) + ->willReturn( + $contentServiceMock = $this + ->getMockBuilder(ContentService::class) + ->disableOriginalConstructor() + ->getMock() ); /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ @@ -626,18 +618,17 @@ public function testFindSingle() $permissionsCriterionResolverMock->expects(self::once()) ->method('getPermissionsCriterion') ->with('content', 'read') - ->will(self::returnValue(true)); + ->willReturn(true); $languageFilter = []; $spiContentInfo = new SPIContentInfo(['id' => 123]); $contentMock = $this->getMockForAbstractClass(Content::class); - /* @var \PHPUnit\Framework\MockObject\MockObject $searchHandlerMock */ $searchHandlerMock ->expects(self::once()) ->method('findSingle') ->with(self::equalTo($criterionMock), self::equalTo($languageFilter)) - ->will(self::returnValue($spiContentInfo)); + ->willReturn($spiContentInfo); $domainMapperMock->expects(self::never()) ->method(self::anything()); @@ -645,7 +636,7 @@ public function testFindSingle() $contentServiceMock ->expects(self::once()) ->method('internalLoadContentById') - ->will(self::returnValue($contentMock)); + ->willReturn($contentMock); $result = $service->findSingle($criterionMock, $languageFilter, true); @@ -829,7 +820,7 @@ public function testFindLocationsBackgroundIndexerWhenDomainMapperThrowsExceptio ->with(self::equalTo($result)) ->willReturnCallback(static function (SearchResult $spiResult) use ($location) { unset($spiResult->searchHits[0]); - --$spiResult->totalCount; + $spiResult->totalCount = $spiResult->totalCount > 0 ? --$spiResult->totalCount : 0; return [$location]; }); From cc9507160ecd08421e698e22e9123a5be690c57a Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 15:03:33 +0100 Subject: [PATCH 15/27] [Pagerfanta][PHPStan] Created Pagerfanta baseline --- phpstan-baseline.pagerfanta.neon | 291 +++++++++++++++++++++++++++++++ phpstan.neon.dist | 1 + 2 files changed, 292 insertions(+) create mode 100644 phpstan-baseline.pagerfanta.neon diff --git a/phpstan-baseline.pagerfanta.neon b/phpstan-baseline.pagerfanta.neon new file mode 100644 index 0000000000..ec81ff0ba4 --- /dev/null +++ b/phpstan-baseline.pagerfanta.neon @@ -0,0 +1,291 @@ +parameters: + ignoreErrors: + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) return type with generic interface Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter does not specify its types\\: T$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Parameter \\#1 \\$searchResult of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ given\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createAdapter\\(\\) return type with generic interface Pagerfanta\\\\Adapter\\\\AdapterInterface does not specify its types\\: T$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createFixedAdapter\\(\\) return type with generic interface Pagerfanta\\\\Adapter\\\\AdapterInterface does not specify its types\\: T$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getNbResults\\(\\) should return int\\<0, max\\> but returns int\\<\\-1, max\\>\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:getNbResults\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:count\\(\\) should return int\\<0, max\\> but returns int\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectsOnSearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-1, max\\>\\.$#" + count: 2 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:internalFindContentInfo\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$totalCount \\(int\\) does not accept int\\<0, max\\>\\|null\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:searchSubGroups\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findLocations\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$time \\(int\\) does not accept float\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept array\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: tests/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:assertSortResult\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:findContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:findLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:getResultContentIdList\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:sortContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:sortLocations\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int\\<0, max\\>\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Parameter \\#1 \\$expectedCount of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\<0, max\\>\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\DeleteTranslationTest\\:\\:findContent\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\AbstractSortClauseTest\\:\\:assertSearchResultOrderByRemoteId\\(\\) has parameter \\$actualSearchResults with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceBookmarkTest\\:\\:extractRemoteIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceBookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:assertFulltextSearch\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapSearchResultToIds\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:mapResultLocationIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:printResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:simplifySearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertSearchResultMatchTranslations\\(\\) has parameter \\$searchResult with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:find\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:mapResultContentIds\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:printResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:simplifySearchResult\\(\\) has parameter \\$result with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult but does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-1, max\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-2, max\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Parameter \\#2 \\$searchHit of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\\\:\\:assertIndexName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Parameter \\#2 \\$searchHit of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\\\:\\:assertIndexName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ given\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Type mixed in generic type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\ in PHPDoc tag @param for parameter \\$searchHit is not subtype of template type T of Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Template type TItem of method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:configureMocks\\(\\) is not referenced in a parameter\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" + count: 1 + path: tests/lib/Pagination/ContentFilteringAdapterTest.php + + - + message: "#^Parameter \\#3 \\$languageFilter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter constructor expects array\\\\|null, array\\\\|true\\> given\\.$#" + count: 2 + path: tests/lib/Pagination/ContentFilteringAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\FixedSearchResultHitAdapterTest\\:\\:createExampleSearchResult\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" + count: 1 + path: tests/lib/Pagination/FixedSearchResultHitAdapterTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8c4caf4f90..8c09fcf747 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,6 +2,7 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-symfony/extension.neon - phpstan-baseline.neon + - phpstan-baseline.pagerfanta.neon parameters: level: 8 From fa21a6a9c93da0afb3618d1c68404af92a31cf86 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 17:04:55 +0100 Subject: [PATCH 16/27] [Pagerfanta][PHPStan] Fixed incorrect persistence findLocations return type --- src/contracts/Search/Handler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/Search/Handler.php b/src/contracts/Search/Handler.php index 20ec7388bf..46e50ff3a8 100644 --- a/src/contracts/Search/Handler.php +++ b/src/contracts/Search/Handler.php @@ -50,7 +50,7 @@ public function findSingle(CriterionInterface $filter, array $languageFilter = [ * * @phpstan-param TSearchLanguageFilter $languageFilter {@see \Ibexa\Contracts\Core\Repository\SearchService::findSingle()} * - * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\Location> */ public function findLocations(LocationQuery $query, array $languageFilter = []): SearchResult; From 55ba1cf1dbbbdd190ca1fad8912ed6b70a6feedf Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 17:05:51 +0100 Subject: [PATCH 17/27] [Pagerfanta][PHPStan] Fixed UserService::searchSubGroups return type --- src/lib/Repository/UserService.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/Repository/UserService.php b/src/lib/Repository/UserService.php index 091ee4df0c..7a97184b0d 100644 --- a/src/lib/Repository/UserService.php +++ b/src/lib/Repository/UserService.php @@ -251,11 +251,11 @@ public function loadSubUserGroups(APIUserGroup $userGroup, int $offset = 0, int /** * Returns (searches) subgroups of a user group described by its main location. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location - * @param int $offset - * @param int $limit + * @phpstan-return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Repository\Values\Content\Location> * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function searchSubGroups(Location $location, int $offset = 0, int $limit = 25): SearchResult { From 481fd02b60c292771a17c1355545523ed4085817 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 10 Dec 2024 17:16:22 +0100 Subject: [PATCH 18/27] [Pagerfanta]Fixed LSE Handler findContent & findLocation impl. type hints --- src/lib/Search/Legacy/Content/Handler.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php index 7afb917ad5..4a18958283 100644 --- a/src/lib/Search/Legacy/Content/Handler.php +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -139,9 +139,10 @@ public function findContent(Query $query, array $languageFilter = []): SearchRes $query->performCount ); + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> $result */ $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; + $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : 0; $contentInfoList = $this->contentMapper->extractContentInfoFromRows( $data['rows'], '', @@ -149,6 +150,7 @@ public function findContent(Query $query, array $languageFilter = []): SearchRes ); foreach ($contentInfoList as $index => $contentInfo) { + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> $searchHit */ $searchHit = new SearchHit(); $searchHit->valueObject = $contentInfo; $searchHit->matchedTranslation = $this->extractMatchedLanguage( @@ -223,12 +225,14 @@ public function findLocations(LocationQuery $query, array $languageFilter = []): $query->performCount ); + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\Location> $result */ $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; + $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : 0; $locationList = $this->locationMapper->createLocationsFromRows($data['rows']); foreach ($locationList as $index => $location) { + /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit<\Ibexa\Contracts\Core\Persistence\Content\Location> $searchHit */ $searchHit = new SearchHit(); $searchHit->valueObject = $location; $searchHit->matchedTranslation = $this->extractMatchedLanguage( From 4804d7a5a9a63a09e31515694e65d6fd867e6426 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 11 Dec 2024 13:52:27 +0100 Subject: [PATCH 19/27] [Pagerfanta] Improved type hints of QueryRenderController --- .../Controller/QueryRenderController.php | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib/MVC/Symfony/Controller/QueryRenderController.php b/src/lib/MVC/Symfony/Controller/QueryRenderController.php index af4f8d2dd7..062190b6fa 100644 --- a/src/lib/MVC/Symfony/Controller/QueryRenderController.php +++ b/src/lib/MVC/Symfony/Controller/QueryRenderController.php @@ -20,14 +20,14 @@ * Controller used internally by ez_query_*_render and ez_query_*_render_* functions. * * @internal + * + * @phpstan-type TOptionsArray array */ final class QueryRenderController { - /** @var \Ibexa\Core\Query\QueryFactoryInterface */ - private $queryFactory; + private QueryFactoryInterface $queryFactory; - /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface */ - private $searchHitAdapterFactory; + private SearchHitAdapterFactoryInterface $searchHitAdapterFactory; public function __construct( QueryFactoryInterface $queryFactory, @@ -37,6 +37,11 @@ public function __construct( $this->searchHitAdapterFactory = $searchHitAdapterFactory; } + /** + * @phpstan-param TOptionsArray $options + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function renderQuery(Request $request, array $options): QueryView { $options = $this->resolveOptions($options); @@ -57,6 +62,11 @@ public function renderQuery(Request $request, array $options): QueryView ); } + /** + * @phpstan-param TOptionsArray $options + * + * @phpstan-return TOptionsArray + */ private function resolveOptions(array $options): array { $resolver = new OptionsResolver(); @@ -92,6 +102,11 @@ private function resolveOptions(array $options): array return $resolver->resolve($options); } + /** + * @phpstan-param TOptionsArray $options + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ private function getAdapter(array $options): SearchResultAdapter { $query = $this->queryFactory->create( From 44fb5498d250e93bc158ebdf24ff59d9ff17b81a Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 11 Dec 2024 13:53:00 +0100 Subject: [PATCH 20/27] [Pagerfanta][Tests] Improved QueryRenderControllerTest --- .../Controller/QueryRenderControllerTest.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php index c4ac98007d..18394243ef 100644 --- a/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php +++ b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php @@ -15,11 +15,16 @@ use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use Ibexa\Core\Query\QueryFactoryInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; /** - * @phpstan-type TOptionsArray array + * @covers \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController + * + * @phpstan-import-type TOptionsArray from \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController + * + * @template TSearchHitValueObject of \Ibexa\Contracts\Core\Repository\Values\ValueObject */ final class QueryRenderControllerTest extends TestCase { @@ -53,14 +58,11 @@ final class QueryRenderControllerTest extends TestCase ], ]; - /** @var \Ibexa\Core\Query\QueryFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $queryFactory; + private QueryFactoryInterface & MockObject $queryFactory; - /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $searchHitAdapterFactory; + private SearchHitAdapterFactoryInterface & MockObject $searchHitAdapterFactory; - /** @var \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController */ - private $controller; + private QueryRenderController $controller; protected function setUp(): void { From 2b768e8e35d2eff96f63850976f7dd481b562df2 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 11 Dec 2024 13:55:00 +0100 Subject: [PATCH 21/27] [Pagerfanta] Fixed incorrect return types of SearchHitAdapterFactory methods --- .../SearchHitAdapterFactory.php | 20 ++++--------------- .../SearchHitAdapterFactoryInterface.php | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php index 3dad98abab..9bc7e2e668 100644 --- a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php @@ -14,7 +14,7 @@ use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use Pagerfanta\Adapter\AdapterInterface; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; /** * @internal @@ -24,19 +24,14 @@ final class SearchHitAdapterFactory implements SearchHitAdapterFactoryInterface { /** @var \Ibexa\Contracts\Core\Repository\SearchService */ - private $searchService; + private SearchService $searchService; public function __construct(SearchService $searchService) { $this->searchService = $searchService; } - /** - * @phpstan-param TSearchLanguageFilter $languageFilter - * - * @phpstan-return \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\ValueObject> - */ - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface + public function createAdapter(Query $query, array $languageFilter = []): SearchResultAdapter { if ($query instanceof LocationQuery) { return new LocationSearchHitAdapter($query, $this->searchService, $languageFilter); @@ -45,14 +40,7 @@ public function createAdapter(Query $query, array $languageFilter = []): Adapter return new ContentSearchHitAdapter($query, $this->searchService, $languageFilter); } - /** - * @phpstan-return \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\ValueObject> - * - * @phpstan-param TSearchLanguageFilter $languageFilter - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException - */ - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface + public function createFixedAdapter(Query $query, array $languageFilter = []): SearchResultAdapter { if ($query instanceof LocationQuery) { $searchResults = $this->searchService->findLocations($query, $languageFilter); diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php index 7717cfc618..98d69a24b1 100644 --- a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php @@ -9,14 +9,28 @@ namespace Ibexa\Core\Pagination\Pagerfanta\AdapterFactory; use Ibexa\Contracts\Core\Repository\Values\Content\Query; -use Pagerfanta\Adapter\AdapterInterface; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; /** * @internal + * + * @phpstan-import-type TSearchLanguageFilter from \Ibexa\Contracts\Core\Repository\SearchService */ interface SearchHitAdapterFactoryInterface { - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface; + /** + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + */ + public function createAdapter(Query $query, array $languageFilter = []): SearchResultAdapter; - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface; + /** + * @phpstan-return \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter<\Ibexa\Contracts\Core\Repository\Values\ValueObject> + * + * @phpstan-param TSearchLanguageFilter $languageFilter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function createFixedAdapter(Query $query, array $languageFilter = []): SearchResultAdapter; } From 0a8ed888b165771c0addd1a82318063be918f72c Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 11 Dec 2024 13:55:21 +0100 Subject: [PATCH 22/27] [Pagerfanta][PHPStan] Removed resolved issues from the main baseline --- phpstan-baseline.neon | 561 +----------------------------------------- 1 file changed, 3 insertions(+), 558 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ac46a82e5e..9a103ed8af 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -40,16 +40,6 @@ parameters: count: 1 path: src/bundle/Core/Command/CopySubtreeCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:getAllChildrenCount\\(\\) should return int but returns int\\|null\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - - - message: "#^PHPDoc tag @return with type int\\|null is not subtype of native type int\\.$#" - count: 1 - path: src/bundle/Core/Command/CopySubtreeCommand.php - - message: "#^Parameter \\#1 \\$messages of method Symfony\\\\Component\\\\Console\\\\Output\\\\OutputInterface\\:\\:write\\(\\) expects iterable\\\\|string, string\\|false given\\.$#" count: 1 @@ -170,16 +160,6 @@ parameters: count: 1 path: src/bundle/Core/Command/ReindexCommand.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$fields\\.$#" - count: 1 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - - - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:getVersionInfo\\(\\)\\.$#" - count: 2 - path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" count: 1 @@ -2630,11 +2610,6 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/RoleContext.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Dead catch \\- Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\NotFoundException is never thrown in the try block\\.$#" count: 1 @@ -2870,21 +2845,11 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/UserContext.php - - - message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\\\|int, string given\\.$#" - count: 1 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Features/Context/UserContext.php - - - message: "#^Parameter \\#2 \\$parentLocationId of method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserGroups\\(\\) expects string\\|null, int given\\.$#" - count: 1 - path: src/bundle/Core/Features/Context/UserContext.php - - message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" count: 1 @@ -4835,26 +4800,6 @@ parameters: count: 1 path: src/contracts/Repository/Decorator/LocationServiceDecorator.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/SearchServiceDecorator.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -6045,11 +5990,6 @@ parameters: count: 1 path: src/contracts/Repository/Iterator/BatchIterator.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" count: 1 @@ -6065,16 +6005,6 @@ parameters: count: 1 path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php - - - - message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" - count: 1 - path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LanguageResolver\\:\\:getPrioritizedLanguages\\(\\) has parameter \\$forcedLanguages with no value type specified in iterable type array\\.$#" count: 1 @@ -6125,26 +6055,6 @@ parameters: count: 1 path: src/contracts/Repository/PermissionCriterionResolver.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/SearchService.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -6195,11 +6105,6 @@ parameters: count: 1 path: src/contracts/Repository/Values/Content/ContentDraftList.php - - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/contracts/Repository/Values/Content/ContentList.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\DraftList\\\\Item\\\\UnauthorizedContentDraftListItem\\:\\:__construct\\(\\) has parameter \\$payload with no value type specified in iterable type array\\.$#" count: 1 @@ -6470,11 +6375,6 @@ parameters: count: 1 path: src/contracts/Repository/Values/Content/Search/Facet/UserFacet.php - - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/contracts/Repository/Values/Content/Search/SearchResult.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" count: 1 @@ -6660,21 +6560,6 @@ parameters: count: 1 path: src/contracts/Search/Handler.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Search/Handler.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" count: 1 @@ -10125,16 +10010,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - - message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - - - message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\QueryController\\:\\:getAdapter\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query, Ibexa\\\\Core\\\\QueryType\\\\QueryType given\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/Content/QueryController.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has no return type specified\\.$#" count: 1 @@ -10230,31 +10105,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) should return Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter but returns Pagerfanta\\\\Adapter\\\\AdapterInterface\\.$#" - count: 2 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:renderQuery\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/MVC/Symfony/Controller/QueryRenderController.php - - message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\SecurityController\\:\\:loginAction\\(\\) has no return type specified\\.$#" count: 1 @@ -11995,71 +11845,6 @@ parameters: count: 1 path: src/lib/MVC/Symfony/View/ViewManagerInterface.php - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getNbResults\\(\\) should return int but returns int\\|null\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php - - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should return array\\ but returns array\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php - - - - message: "#^Return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getAggregations\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection\\|null\\.$#" count: 1 @@ -12070,56 +11855,6 @@ parameters: count: 1 path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php - - - message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should return array\\ but returns array\\, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php - - - - message: "#^Return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getAggregations\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getMaxScore\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTime\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTimedOut\\(\\)\\.$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - - - message: "#^Class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta extends generic class Pagerfanta\\\\Pagerfanta but does not specify its types\\: T$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/Pagerfanta.php - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractHandler\\:\\:getMultipleCacheItems\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" count: 1 @@ -17605,16 +17340,6 @@ parameters: count: 1 path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - - message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:map\\(\\) should return Ibexa\\\\Core\\\\QueryType\\\\QueryType but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\.$#" - count: 1 - path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - - - message: "#^Parameter \\#2 \\$code of class InvalidArgumentException constructor expects int, string given\\.$#" - count: 1 - path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php - - message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryType\\:\\:getQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 @@ -18570,11 +18295,6 @@ parameters: count: 1 path: src/lib/Repository/SearchService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SearchService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" count: 1 @@ -18690,26 +18410,6 @@ parameters: count: 1 path: src/lib/Repository/SiteAccessAware/Repository.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/SearchService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" count: 1 @@ -18930,16 +18630,6 @@ parameters: count: 2 path: src/lib/Repository/URLService.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$items \\(array\\\\) does not accept array\\\\.$#" - count: 1 - path: src/lib/Repository/URLService.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$totalCount \\(int\\) does not accept int\\|null\\.$#" - count: 1 - path: src/lib/Repository/URLService.php - - message: "#^Property Ibexa\\\\Core\\\\Repository\\\\URLService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" count: 1 @@ -18970,11 +18660,6 @@ parameters: count: 1 path: src/lib/Repository/UserPreferenceService.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: src/lib/Repository/UserService.php - - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\)\\.$#" count: 5 @@ -20360,26 +20045,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Handler.php - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" count: 1 @@ -20405,16 +20070,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Handler.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$time \\(int\\) does not accept float\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$totalCount \\(int\\|null\\) does not accept array\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Indexer\\:\\:purge\\(\\) has no return type specified\\.$#" count: 1 @@ -30985,11 +30640,6 @@ parameters: count: 2 path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php - - - message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int, int\\|null given\\.$#" - count: 1 - path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php - - message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\\\|int, int\\|null given\\.$#" count: 1 @@ -31290,11 +30940,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/integration/Core/Repository/LocationServiceTest.php - - message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" count: 11 @@ -32390,11 +32035,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php - - - message: "#^Parameter \\#1 \\$expectedCount of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018VisibilityTest\\:\\:testSearchForHiddenContent\\(\\) has no return type specified\\.$#" count: 1 @@ -33280,31 +32920,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/RoleServiceTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 4 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$hidden\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 17 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$invisible\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$versionInfo\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchEngineIndexingTest.php - - message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$mainLocationId\\.$#" count: 1 @@ -33765,16 +33380,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php - - message: "#^Cannot access offset 0 on array\\\\|bool\\|float\\|int\\|string\\.$#" count: 1 @@ -33870,11 +33475,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindContent\\(\\) has no return type specified\\.$#" count: 1 @@ -34007,7 +33607,7 @@ parameters: - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 19 + count: 1 path: tests/integration/Core/Repository/SearchServiceLocationTest.php - @@ -34165,19 +33765,9 @@ parameters: count: 2 path: tests/integration/Core/Repository/SearchServiceLocationTest.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceLocationTest.php - - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentId\\.$#" - count: 4 - path: tests/integration/Core/Repository/SearchServiceTest.php - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 36 + count: 3 path: tests/integration/Core/Repository/SearchServiceTest.php - @@ -34195,11 +33785,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchServiceTest.php - - - message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\|null\\.$#" - count: 3 - path: tests/integration/Core/Repository/SearchServiceTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ContentTypeIdentifier constructor invoked with 2 parameters, 1 required\\.$#" count: 2 @@ -35015,11 +34600,6 @@ parameters: count: 3 path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:assertIndexName\\(\\) has parameter \\$indexMap with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createContent\\(\\) has parameter \\$parentLocationIds with no value type specified in iterable type array\\.$#" count: 1 @@ -35040,16 +34620,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has parameter \\$indexMap with no type specified\\.$#" - count: 1 - path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexesToMatchData\\(\\) has parameter \\$inputContentData with no value type specified in iterable type array\\.$#" count: 1 @@ -42355,21 +41925,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Controller/ControllerTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:assertRenderQueryResult\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:configureMocks\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - - - message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" - count: 2 - path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEventTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" count: 1 @@ -44770,66 +44325,6 @@ parameters: count: 1 path: tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/ContentSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Pagination/LocationSearchHitAdapterTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:getCacheItem\\(\\) has parameter \\$key with no type specified\\.$#" count: 1 @@ -53055,31 +52550,6 @@ parameters: count: 1 path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentInfoSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php - - - - message: "#^Parameter \\#2 \\$query of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" - count: 1 - path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorTestAdapter\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -54907,7 +54377,7 @@ parameters: - message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:expects\\(\\)\\.$#" - count: 8 + count: 7 path: tests/lib/Repository/Service/Mock/SearchTest.php - @@ -54995,11 +54465,6 @@ parameters: count: 1 path: tests/lib/Repository/Service/Mock/SearchTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingle\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/Repository/Service/Mock/SearchTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleThrowsHandlerException\\(\\) has no return type specified\\.$#" count: 1 @@ -57145,11 +56610,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/AbstractTestCase.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 8 - path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 @@ -57210,11 +56670,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 2 - path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 @@ -57555,11 +57010,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 3 - path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 @@ -57670,11 +57120,6 @@ parameters: count: 1 path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - - message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" - count: 1 - path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 From 7d97e729da8ecb85f1646b0d2129d0c3a5d507df Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Wed, 11 Dec 2024 13:55:43 +0100 Subject: [PATCH 23/27] [Pagerfanta][PHPStan] Aligned Pagerfanta baseline with resolved issues --- phpstan-baseline.pagerfanta.neon | 50 -------------------------------- 1 file changed, 50 deletions(-) diff --git a/phpstan-baseline.pagerfanta.neon b/phpstan-baseline.pagerfanta.neon index ec81ff0ba4..d4b009bae5 100644 --- a/phpstan-baseline.pagerfanta.neon +++ b/phpstan-baseline.pagerfanta.neon @@ -10,16 +10,6 @@ parameters: count: 1 path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createAdapter\\(\\) return type with generic interface Pagerfanta\\\\Adapter\\\\AdapterInterface does not specify its types\\: T$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createFixedAdapter\\(\\) return type with generic interface Pagerfanta\\\\Adapter\\\\AdapterInterface does not specify its types\\: T$#" - count: 1 - path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php - - message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getNbResults\\(\\) should return int\\<0, max\\> but returns int\\<\\-1, max\\>\\.$#" count: 1 @@ -80,31 +70,6 @@ parameters: count: 1 path: src/lib/Repository/URLService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:searchSubGroups\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult does not specify its types\\: TSearchHitValueObject$#" - count: 1 - path: src/lib/Repository/UserService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findLocations\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\ but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\.$#" - count: 1 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$time \\(int\\) does not accept float\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept array\\.$#" - count: 2 - path: src/lib/Search/Legacy/Content/Handler.php - - message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" count: 1 @@ -230,11 +195,6 @@ parameters: count: 1 path: tests/integration/Core/Repository/SearchServiceTest.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\\\.$#" - count: 2 - path: tests/integration/Core/Repository/SearchServiceTest.php - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\\\:\\:\\$totalCount \\(int\\<0, max\\>\\|null\\) does not accept int\\<\\-1, max\\>\\.$#" count: 1 @@ -265,16 +225,6 @@ parameters: count: 1 path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" - count: 1 - path: tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" - count: 1 - path: tests/lib/Pagination/BaseLocationSearchResultAdapterTestCase.php - - message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int\\<0, max\\>, int given\\.$#" count: 1 From 014a41d9d7d0eeb2413893774e39546546395303 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Thu, 12 Dec 2024 12:42:22 +0100 Subject: [PATCH 24/27] [Pagerfanta] Fixed LSE total count regression when there's no count to perform --- src/lib/Search/Legacy/Content/Handler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php index 4a18958283..ccf685cce0 100644 --- a/src/lib/Search/Legacy/Content/Handler.php +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -142,7 +142,7 @@ public function findContent(Query $query, array $languageFilter = []): SearchRes /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\ContentInfo> $result */ $result = new SearchResult(); $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms - $result->totalCount = $data['count'] !== null ? (int)$data['count'] : 0; + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : null; $contentInfoList = $this->contentMapper->extractContentInfoFromRows( $data['rows'], '', @@ -228,7 +228,7 @@ public function findLocations(LocationQuery $query, array $languageFilter = []): /** @phpstan-var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult<\Ibexa\Contracts\Core\Persistence\Content\Location> $result */ $result = new SearchResult(); $result->time = (int) (microtime(true) - $start) * 1000; // time expressed in ms - $result->totalCount = $data['count'] !== null ? (int)$data['count'] : 0; + $result->totalCount = $data['count'] !== null ? (int)$data['count'] : null; $locationList = $this->locationMapper->createLocationsFromRows($data['rows']); foreach ($locationList as $index => $location) { From 12d21ce4bd8bac1b323669b9d80418676f1c81c5 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Thu, 12 Dec 2024 16:21:12 +0100 Subject: [PATCH 25/27] Changed infinity to tilde in CopySubtreeCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Parafiński --- src/bundle/Core/Command/CopySubtreeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bundle/Core/Command/CopySubtreeCommand.php b/src/bundle/Core/Command/CopySubtreeCommand.php index bd4b809b5a..c4f6a61f82 100644 --- a/src/bundle/Core/Command/CopySubtreeCommand.php +++ b/src/bundle/Core/Command/CopySubtreeCommand.php @@ -173,6 +173,6 @@ protected function getAllChildrenCountExpr(Location $location): string $totalCount = $this->searchService->findLocations($query)->totalCount; - return $totalCount !== null ? (string) $totalCount : '∞'; + return $totalCount !== null ? (string) $totalCount : '~'; } } From 54ae4133fb1792506a3d3dd56e2b8db36943aca6 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Thu, 12 Dec 2024 16:24:04 +0100 Subject: [PATCH 26/27] Dropped leftover PHPDoc type hints from CopySubtreeCommand --- src/bundle/Core/Command/CopySubtreeCommand.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bundle/Core/Command/CopySubtreeCommand.php b/src/bundle/Core/Command/CopySubtreeCommand.php index c4f6a61f82..d5728566c8 100644 --- a/src/bundle/Core/Command/CopySubtreeCommand.php +++ b/src/bundle/Core/Command/CopySubtreeCommand.php @@ -34,16 +34,12 @@ class CopySubtreeCommand extends Command private LocationService $locationService; - /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private PermissionResolver $permissionResolver; - /** @var \Ibexa\Contracts\Core\Repository\UserService */ private UserService $userService; - /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ private ContentTypeService $contentTypeService; - /** @var \Ibexa\Contracts\Core\Repository\SearchService */ private SearchService $searchService; /** From 17e2c05eb273725cc5f1637e42b2d768058993b0 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Fri, 13 Dec 2024 09:59:58 +0100 Subject: [PATCH 27/27] [Tests][CS] Fixed CS in BaseContentSearchResultAdapterTestCase --- .../Pagination/BaseContentSearchResultAdapterTestCase.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php b/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php index de4bab0c03..60be3dbbb2 100644 --- a/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php +++ b/tests/lib/Pagination/BaseContentSearchResultAdapterTestCase.php @@ -61,9 +61,11 @@ protected function mockSearchHitsForGetSlice( $hits = []; for ($i = 0; $i < self::EXAMPLE_LIMIT; ++$i) { - $hits[] = new SearchHit([ - 'valueObject' => $this->createMock(APIContent::class), - ]); + $hits[] = new SearchHit( + [ + 'valueObject' => $this->createMock(APIContent::class), + ] + ); } $searchResult = new SearchResult(