diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b6600d6..4fd57ba 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,31 +1,19 @@ parameters: ignoreErrors: - - message: '#^Parameter \#1 \$locationId of method Ibexa\\Contracts\\Core\\Repository\\LocationService\:\:loadLocation\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 1 - path: src/bundle/Controller/QueryFieldRestController.php - - - - message: '#^Parameter \#1 \$uri of method Ibexa\\Contracts\\Rest\\UriParser\\UriParserInterface\:\:getAttributeFromUri\(\) expects string, string\|null given\.$#' - identifier: argument.type - count: 1 - path: src/bundle/Controller/QueryFieldRestController.php - - - - message: '#^Argument of an invalid type array\|bool\|float\|int\|string\|null supplied for foreach, only iterables are supported\.$#' + message: '#^Argument of an invalid type array\|bool\|float\|int\|string supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable count: 1 path: src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php - - message: '#^Cannot access offset \(int\|string\) on non\-empty\-array\|bool\|float\|int\|string\|null\.$#' + message: '#^Cannot access offset \(int\|string\) on non\-empty\-array\|bool\|float\|int\|string\.$#' identifier: offsetAccess.nonOffsetAccessible count: 1 path: src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(int\|string\)\: bool\)\|null, Closure\(mixed\)\: \(0\|1\|false\) given\.$#' + message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(int\|string\)\: bool\)\|null, Closure\(string\)\: \(0\|1\|false\) given\.$#' identifier: argument.type count: 1 path: src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php @@ -36,36 +24,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/Compiler/QueryTypesListPass.php - - - message: '#^Parameter \#1 \$input of static method Symfony\\Component\\Yaml\\Yaml\:\:parse\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/bundle/DependencyInjection/IbexaFieldTypeQueryExtension.php - - - - message: '#^Method Ibexa\\FieldTypeQuery\\ContentView\\QueryResultsInjector\:\:__construct\(\) has parameter \$views with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/ContentView/QueryResultsInjector.php - - - - message: '#^Property Ibexa\\FieldTypeQuery\\ContentView\\QueryResultsInjector\:\:\$queryFieldService \(Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldLocationService&Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldServiceInterface\) does not accept Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldServiceInterface\.$#' - identifier: assign.propertyType - count: 1 - path: src/lib/ContentView/QueryResultsInjector.php - - - - message: '#^Property Ibexa\\FieldTypeQuery\\ContentView\\QueryResultsInjector\:\:\$views type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/ContentView/QueryResultsInjector.php - - - - message: '#^Property Ibexa\\FieldTypeQuery\\ExceptionSafeQueryFieldService\:\:\$inner \(Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldLocationService&Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldServiceInterface\) does not accept Ibexa\\Contracts\\FieldTypeQuery\\QueryFieldServiceInterface\.$#' - identifier: assign.propertyType - count: 1 - path: src/lib/ExceptionSafeQueryFieldService.php - - message: '#^Generator expects key type string, string\|null given\.$#' identifier: generator.keyType @@ -78,38 +36,8 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php - - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:isExpression\(\) has parameter \$expression with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: src/lib/QueryFieldService.php - - - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:prepareQuery\(\) has parameter \$extraParameters with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/QueryFieldService.php - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:resolveExpression\(\) has parameter \$variables with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: src/lib/QueryFieldService.php - - - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:resolveParameters\(\) has parameter \$expressions with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/QueryFieldService.php - - - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:resolveParameters\(\) has parameter \$variables with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/QueryFieldService.php - - - - message: '#^Method Ibexa\\FieldTypeQuery\\QueryFieldService\:\:resolveParameters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/lib/QueryFieldService.php diff --git a/spec/ContentView/FieldDefinitionIdentifierMatcherSpec.php b/spec/ContentView/FieldDefinitionIdentifierMatcherSpec.php index d6dde36..44ccb55 100644 --- a/spec/ContentView/FieldDefinitionIdentifierMatcherSpec.php +++ b/spec/ContentView/FieldDefinitionIdentifierMatcherSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery\ContentView; @@ -20,15 +21,15 @@ use Ibexa\FieldTypeQuery\ContentView\FieldDefinitionIdentifierMatcher; use PhpSpec\ObjectBehavior; -class FieldDefinitionIdentifierMatcherSpec extends ObjectBehavior +final class FieldDefinitionIdentifierMatcherSpec extends ObjectBehavior { - private const CONTENT_TYPE_ID_WITHOUT_FIELD_DEFINITION = 2; - private const CONTENT_TYPE_IDENTIFIER_WITHOUT_FIELD_DEFINITION = 'type_matching_without_field_def'; + private const int CONTENT_TYPE_ID_WITHOUT_FIELD_DEFINITION = 2; + private const string CONTENT_TYPE_IDENTIFIER_WITHOUT_FIELD_DEFINITION = 'type_matching_without_field_def'; - private const CONTENT_TYPE_ID_WITH_FIELD_DEFINITION = 3; - private const CONTENT_TYPE_IDENTIFIER_WITH_FIELD_DEFINITION = 'type_matching_with_field_def'; + private const int CONTENT_TYPE_ID_WITH_FIELD_DEFINITION = 3; + private const string CONTENT_TYPE_IDENTIFIER_WITH_FIELD_DEFINITION = 'type_matching_with_field_def'; - public const FIELD_DEFINITION_IDENTIFIER = 'field_definition'; + public const string FIELD_DEFINITION_IDENTIFIER = 'field_definition'; public function it_is_initializable(): void { @@ -71,7 +72,13 @@ private function buildView(int $contentTypeId): ContentView $view->setContent( new Content([ 'versionInfo' => new VersionInfo([ - 'contentInfo' => new ContentInfo(['contentTypeId' => $contentTypeId]), + 'contentInfo' => new ContentInfo([ + 'contentTypeId' => $contentTypeId, + 'contentType' => new ContentType([ + 'id' => $contentTypeId, + 'identifier' => 'foo_content_type', + ]), + ]), ]), ]) ); diff --git a/spec/ContentView/QueryResultsInjectorSpec.php b/spec/ContentView/QueryResultsInjectorSpec.php index 80472da..049e8f1 100644 --- a/spec/ContentView/QueryResultsInjectorSpec.php +++ b/spec/ContentView/QueryResultsInjectorSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery\ContentView; @@ -12,7 +13,6 @@ use Ibexa\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; use Ibexa\Core\MVC\Symfony\View\ViewEvents; use Ibexa\Core\Repository\Values\Content\Content; -use Ibexa\FieldTypeQuery\ContentView\QueryResultsInjector; use Pagerfanta\Pagerfanta; use PhpSpec\ObjectBehavior; use Prophecy\Argument; @@ -20,13 +20,13 @@ use Symfony\Component\HttpFoundation\RequestStack; use Webmozart\Assert\Assert; -class QueryResultsInjectorSpec extends ObjectBehavior +final class QueryResultsInjectorSpec extends ObjectBehavior { - public const FIELD_VIEW = 'content_query_field'; - public const OTHER_VIEW = 'anything_else'; - public const ITEM_VIEW = 'line'; - public const VIEWS = ['field' => self::FIELD_VIEW, 'item' => self::ITEM_VIEW]; - public const FIELD_DEFINITION_IDENTIFIER = 'query_field'; + public const string FIELD_VIEW = 'content_query_field'; + public const string OTHER_VIEW = 'anything_else'; + public const string ITEM_VIEW = 'line'; + public const array VIEWS = ['field' => self::FIELD_VIEW, 'item' => self::ITEM_VIEW]; + public const string FIELD_DEFINITION_IDENTIFIER = 'query_field'; private ContentView $view; @@ -50,11 +50,6 @@ public function __construct() ); } - public function it_is_initializable(): void - { - $this->shouldHaveType(QueryResultsInjector::class); - } - public function let( QueryFieldServiceInterface $queryFieldService, FilterViewParametersEvent $event, @@ -104,8 +99,9 @@ public function it_does_nothing_for_non_field_views(QueryFieldServiceInterface $ $queryFieldService->getPaginationConfiguration(Argument::any())->shouldNotHaveBeenCalled(); } - public function it_adds_the_query_results_for_the_field_view_without_pagination(QueryFieldServiceInterface $queryFieldService): void - { + public function it_adds_the_query_results_for_the_field_view_without_pagination( + QueryFieldServiceInterface $queryFieldService + ): void { $content = $this->createContentItem(); $queryFieldService @@ -128,6 +124,9 @@ public function it_adds_the_query_results_for_the_field_view_without_pagination( Assert::eq($parameters->get('items'), $this->getResults()); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function it_adds_the_query_results_for_the_field_view_with_pagination( FilterViewParametersEvent $event, QueryFieldServiceInterface $queryFieldService @@ -156,6 +155,9 @@ public function it_adds_the_query_results_for_the_field_view_with_pagination( Assert::isInstanceOf($parameters->get('items'), Pagerfanta::class); } + /** + * @return array + */ public function getMatchers(): array { return [ @@ -170,6 +172,9 @@ private function createContentItem(): Content return new Content(); } + /** + * @return \Ibexa\Core\Repository\Values\Content\Content[] + */ private function getResults(): array { return [ diff --git a/spec/ExceptionSafeQueryFieldServiceSpec.php b/spec/ExceptionSafeQueryFieldServiceSpec.php index 8a82f77..2c85408 100644 --- a/spec/ExceptionSafeQueryFieldServiceSpec.php +++ b/spec/ExceptionSafeQueryFieldServiceSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery; @@ -13,7 +14,7 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; -class ExceptionSafeQueryFieldServiceSpec extends ObjectBehavior +final class ExceptionSafeQueryFieldServiceSpec extends ObjectBehavior { public function let(QueryFieldServiceInterface $queryFieldService): void { diff --git a/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php b/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php index 8cfe62c..ab67f16 100644 --- a/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php +++ b/spec/GraphQL/ContentQueryFieldDefinitionMapperSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery\GraphQL; @@ -15,12 +16,12 @@ use Ibexa\GraphQL\Schema\Domain\Content\NameHelper; use PhpSpec\ObjectBehavior; -class ContentQueryFieldDefinitionMapperSpec extends ObjectBehavior +final class ContentQueryFieldDefinitionMapperSpec extends ObjectBehavior { - public const FIELD_IDENTIFIER = 'test'; - public const FIELD_TYPE_IDENTIFIER = 'ibexa_content_query'; - public const RETURNED_CONTENT_TYPE_IDENTIFIER = 'folder'; - public const GRAPHQL_TYPE = 'FolderContent'; + public const string FIELD_IDENTIFIER = 'test'; + public const string FIELD_TYPE_IDENTIFIER = 'ibexa_content_query'; + public const string RETURNED_CONTENT_TYPE_IDENTIFIER = 'folder'; + public const string GRAPHQL_TYPE = 'FolderContent'; public function let( FieldDefinitionMapper $innerMapper, @@ -100,11 +101,6 @@ public function it_maps_the_field_value_when_pagination_is_enabled(FieldDefiniti ->shouldBe('@=resolver("QueryFieldValueConnection", [args, field, content])'); } - /** - * @param bool $enablePagination - * - * @return \Ibexa\Core\Repository\Values\ContentType\FieldDefinition - */ private function fieldDefinition(bool $enablePagination = false): FieldDefinition { return new FieldDefinition([ @@ -117,9 +113,6 @@ private function fieldDefinition(bool $enablePagination = false): FieldDefinitio ]); } - /** - * @return \Ibexa\Core\Repository\Values\ContentType\FieldDefinition - */ protected function getLambdaFieldDefinition(): FieldDefinition { return new FieldDefinition(['fieldTypeIdentifier' => 'lambda']); diff --git a/spec/GraphQL/QueryFieldResolverSpec.php b/spec/GraphQL/QueryFieldResolverSpec.php index 7303cc2..7ea8649 100644 --- a/spec/GraphQL/QueryFieldResolverSpec.php +++ b/spec/GraphQL/QueryFieldResolverSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery\GraphQL; @@ -13,9 +14,9 @@ use Ibexa\GraphQL\Value\Field; use PhpSpec\ObjectBehavior; -class QueryFieldResolverSpec extends ObjectBehavior +final class QueryFieldResolverSpec extends ObjectBehavior { - public const FIELD_DEFINITION_IDENTIFIER = 'test'; + public const string FIELD_DEFINITION_IDENTIFIER = 'test'; public function let(QueryFieldServiceInterface $queryFieldService): void { diff --git a/spec/Persistence/Legacy/Content/FieldValue/Converter/QueryConverterSpec.php b/spec/Persistence/Legacy/Content/FieldValue/Converter/QueryConverterSpec.php index f594991..980526f 100644 --- a/spec/Persistence/Legacy/Content/FieldValue/Converter/QueryConverterSpec.php +++ b/spec/Persistence/Legacy/Content/FieldValue/Converter/QueryConverterSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery\Persistence\Legacy\Content\FieldValue\Converter; @@ -13,13 +14,13 @@ use PhpSpec\ObjectBehavior; use Webmozart\Assert\Assert; -class QueryConverterSpec extends ObjectBehavior +final class QueryConverterSpec extends ObjectBehavior { - public const PARAMETERS = ['param1' => 'value1', 'param2' => 'value2']; - public const QUERY_TYPE = 'SomeQueryType'; - public const RETURNED_TYPE = 'folder'; - public const ENABLE_PAGINATION = true; - public const ITEMS_PER_PAGE = 10; + public const array PARAMETERS = ['param1' => 'value1', 'param2' => 'value2']; + public const string QUERY_TYPE = 'SomeQueryType'; + public const string RETURNED_TYPE = 'folder'; + public const bool ENABLE_PAGINATION = true; + public const int ITEMS_PER_PAGE = 10; public function it_is_initializable(): void { diff --git a/spec/QueryFieldServiceSpec.php b/spec/QueryFieldServiceSpec.php index 67b3368..1a6918b 100644 --- a/spec/QueryFieldServiceSpec.php +++ b/spec/QueryFieldServiceSpec.php @@ -4,6 +4,7 @@ * @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 spec\Ibexa\FieldTypeQuery; @@ -21,16 +22,15 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; -class QueryFieldServiceSpec extends ObjectBehavior +final class QueryFieldServiceSpec extends ObjectBehavior { - public const CONTENT_TYPE_ID = 1; - public const CONTENT_TYPE_ID_WITHOUT_PAGINATION = 2; - public const LOCATION_ID = 1; - public const QUERY_TYPE_IDENTIFIER = 'query_type_identifier'; - public const FIELD_DEFINITION_IDENTIFIER = 'test'; - - private ?SearchResult $searchResult = null; + public const int CONTENT_TYPE_ID = 1; + public const int CONTENT_TYPE_ID_WITHOUT_PAGINATION = 2; + public const int LOCATION_ID = 1; + public const string QUERY_TYPE_IDENTIFIER = 'query_type_identifier'; + public const string FIELD_DEFINITION_IDENTIFIER = 'test'; + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[]|null */ private ?array $searchHits = null; private int $totalCount = 0; @@ -42,7 +42,7 @@ public function let( QueryType $queryType ): void { $this->searchHits = []; - $this->searchResult = new SearchResult(['searchHits' => $this->searchHits, 'totalCount' => $this->totalCount]); + $searchResult = new SearchResult(['searchHits' => $this->searchHits, 'totalCount' => $this->totalCount]); $parameters = [ 'param1' => 'value1', @@ -58,7 +58,7 @@ public function let( $queryTypeRegistry->getQueryType(self::QUERY_TYPE_IDENTIFIER)->willReturn($queryType); $queryType->getQuery(Argument::any())->willReturn(new ApiQuery()); // @todo this should fail. It does not. - $searchService->findContent(Argument::any())->willReturn($this->searchResult); + $searchService->findContent(Argument::any())->willReturn($searchResult); $this->beConstructedWith($searchService, $contentTypeService, $queryTypeRegistry); } @@ -119,9 +119,6 @@ public function it_returns_the_items_per_page_number_as_pagination_configuration )->shouldBe(10); } - /** - * @return \Ibexa\Core\Repository\Values\Content\Content - */ private function getContent(int $contentTypeId = self::CONTENT_TYPE_ID): Values\Content\Content { return new Values\Content\Content([ @@ -138,13 +135,11 @@ private function getContent(int $contentTypeId = self::CONTENT_TYPE_ID): Values\ } /** - * @param array $parameters - * - * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * @param array $parameters */ private function getContentType(array $parameters, bool $enablePagination = true, int $itemsPerPage = 10): ContentType { - $contentType = new Values\ContentType\ContentType([ + return new Values\ContentType\ContentType([ 'fieldDefinitions' => new Values\ContentType\FieldDefinitionCollection([ new Values\ContentType\FieldDefinition([ 'identifier' => self::FIELD_DEFINITION_IDENTIFIER, @@ -159,7 +154,5 @@ private function getContentType(array $parameters, bool $enablePagination = true ]), ]), ]); - - return $contentType; } } diff --git a/src/bundle/Controller/QueryFieldRestController.php b/src/bundle/Controller/QueryFieldRestController.php index ee9a753..ab78777 100644 --- a/src/bundle/Controller/QueryFieldRestController.php +++ b/src/bundle/Controller/QueryFieldRestController.php @@ -4,6 +4,7 @@ * @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\Bundle\FieldTypeQuery\Controller; @@ -22,36 +23,24 @@ use Ibexa\Rest\Server\Values\RestContent; use Symfony\Component\HttpFoundation\Request; -final class QueryFieldRestController +final readonly class QueryFieldRestController { - private QueryFieldService $queryFieldService; - - private ContentService $contentService; - - private ContentTypeService $contentTypeService; - - private LocationService $locationService; - - private UriParserInterface $uriParser; - - private ContentService\RelationListFacadeInterface $relationListFacade; - public function __construct( - QueryFieldService $queryFieldService, - ContentService $contentService, - ContentTypeService $contentTypeService, - LocationService $locationService, - UriParserInterface $uriParser, - ContentService\RelationListFacadeInterface $relationListFacade + private QueryFieldService $queryFieldService, + private ContentService $contentService, + private ContentTypeService $contentTypeService, + private LocationService $locationService, + private UriParserInterface $uriParser, + private ContentService\RelationListFacadeInterface $relationListFacade ) { - $this->queryFieldService = $queryFieldService; - $this->contentService = $contentService; - $this->contentTypeService = $contentTypeService; - $this->locationService = $locationService; - $this->uriParser = $uriParser; - $this->relationListFacade = $relationListFacade; } + /** + * @throws \Ibexa\Contracts\Rest\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ public function getResults( Request $request, int $contentId, @@ -89,11 +78,18 @@ public function getResults( return new RestValues\ContentList( array_map( function (Content $content): RestContent { + $contentInfo = $content->getContentInfo(); + if ($contentInfo->getMainLocationId() === null) { + throw new NotFoundException( + sprintf('Content with ID "%s" has no main location.', $contentInfo->getId()) + ); + } + return new RestContent( - $content->contentInfo, - $this->locationService->loadLocation($content->contentInfo->mainLocationId), + $content->getContentInfo(), + $this->locationService->loadLocation($contentInfo->getMainLocationId()), $content, - $this->getContentType($content->contentInfo), + $this->getContentType($contentInfo), iterator_to_array($this->relationListFacade->getRelations($content->getVersionInfo())) ); }, @@ -116,11 +112,19 @@ private function getContentType(ContentInfo $contentInfo): ContentType /** * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function loadLocationByPath(Request $request): Location { - $locationHrefParts = explode('/', $this->uriParser->getAttributeFromUri($request->query->get('location'), 'locationPath')); + $locationHrefParts = explode( + '/', + $this->uriParser->getAttributeFromUri( + $request->query->get('location') ?? '', + 'locationPath' + ) + ); + $locationId = array_pop($locationHrefParts); return $this->locationService->loadLocation((int)$locationId); diff --git a/src/bundle/DependencyInjection/Compiler/ConfigurableFieldDefinitionMapperPass.php b/src/bundle/DependencyInjection/Compiler/ConfigurableFieldDefinitionMapperPass.php index 026139b..61f694b 100644 --- a/src/bundle/DependencyInjection/Compiler/ConfigurableFieldDefinitionMapperPass.php +++ b/src/bundle/DependencyInjection/Compiler/ConfigurableFieldDefinitionMapperPass.php @@ -14,9 +14,9 @@ * Adds the configuration for the query field type to the ibexa.graphql.schema.content.mapping.field_definition_type * configuration variable. */ -class ConfigurableFieldDefinitionMapperPass implements CompilerPassInterface +final class ConfigurableFieldDefinitionMapperPass implements CompilerPassInterface { - public const PARAMETER = 'ibexa.graphql.schema.content.mapping.field_definition_type'; + public const string PARAMETER = 'ibexa.graphql.schema.content.mapping.field_definition_type'; public function process(ContainerBuilder $container): void { diff --git a/src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php b/src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php index d909db5..b10f084 100644 --- a/src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php +++ b/src/bundle/DependencyInjection/Compiler/FieldDefinitionIdentifierViewMatcherPass.php @@ -4,6 +4,7 @@ * @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\Bundle\FieldTypeQuery\DependencyInjection\Compiler; @@ -14,22 +15,22 @@ /** * Replaces the short alias 'Identifier\FieldDefinition' by the matcher's service. */ -class FieldDefinitionIdentifierViewMatcherPass implements CompilerPassInterface +final class FieldDefinitionIdentifierViewMatcherPass implements CompilerPassInterface { - private const LONG_IDENTIFIER = '@' . FieldDefinitionIdentifierMatcher::class; - private const SHORT_IDENTIFIER = 'Identifier\FieldDefinition'; + private const string LONG_IDENTIFIER = '@' . FieldDefinitionIdentifierMatcher::class; + private const string SHORT_IDENTIFIER = 'Identifier\FieldDefinition'; public function process(ContainerBuilder $container): void { $configKeys = array_filter( array_keys($container->getParameterBag()->all()), - static function ($parameterName): int|false { + static function (string $parameterName): int|false { return preg_match('/ibexa.site_access.config\..+\.content_view/', $parameterName); } ); foreach ($configKeys as $configKey) { - $configuration = $container->getParameter($configKey); + $configuration = $container->getParameter($configKey) ?? []; foreach ($configuration as $viewType => $viewConfigurations) { foreach ($viewConfigurations as $viewConfigurationName => $viewConfiguration) { if (isset($viewConfiguration['match'][self::SHORT_IDENTIFIER])) { diff --git a/src/bundle/DependencyInjection/Compiler/QueryTypesListPass.php b/src/bundle/DependencyInjection/Compiler/QueryTypesListPass.php index 7ad6b0e..b243d90 100644 --- a/src/bundle/DependencyInjection/Compiler/QueryTypesListPass.php +++ b/src/bundle/DependencyInjection/Compiler/QueryTypesListPass.php @@ -4,6 +4,7 @@ * @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\Bundle\FieldTypeQuery\DependencyInjection\Compiler; @@ -13,7 +14,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; -class QueryTypesListPass implements CompilerPassInterface +final class QueryTypesListPass implements CompilerPassInterface { private CamelCaseToSnakeCaseNameConverter $nameConverter; @@ -44,7 +45,7 @@ public function process(ContainerBuilder $container): void } /** - * Builds a human readable name out of a query type identifier. + * Builds a human-readable name out of a query type identifier. */ private function buildQueryTypeName(string $queryTypeIdentifier): string { diff --git a/src/bundle/DependencyInjection/IbexaFieldTypeQueryExtension.php b/src/bundle/DependencyInjection/IbexaFieldTypeQueryExtension.php index e2a080b..ef49bfd 100644 --- a/src/bundle/DependencyInjection/IbexaFieldTypeQueryExtension.php +++ b/src/bundle/DependencyInjection/IbexaFieldTypeQueryExtension.php @@ -4,6 +4,7 @@ * @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\Bundle\FieldTypeQuery\DependencyInjection; @@ -41,9 +42,6 @@ public function prepend(ContainerBuilder $container): void $this->prependGraphQL($container); } - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ protected function addContentViewConfig(ContainerBuilder $container): void { $contentViewDefaults = $container->getParameter('ibexa.site_access.config.default.content_view_defaults'); @@ -92,7 +90,7 @@ private function prependJMSTranslationConfig(ContainerBuilder $container): void protected function prependFieldTemplateConfig(ContainerBuilder $container): void { $configFile = __DIR__ . '/../Resources/config/field_templates.yaml'; - $config = Yaml::parse(file_get_contents($configFile)); + $config = Yaml::parseFile($configFile); $container->prependExtensionConfig('ibexa', $config); $container->addResource(new FileResource($configFile)); } diff --git a/src/bundle/IbexaFieldTypeQueryBundle.php b/src/bundle/IbexaFieldTypeQueryBundle.php index 6e09579..8518e98 100644 --- a/src/bundle/IbexaFieldTypeQueryBundle.php +++ b/src/bundle/IbexaFieldTypeQueryBundle.php @@ -4,6 +4,7 @@ * @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\Bundle\FieldTypeQuery; diff --git a/src/bundle/Resources/config/services.yaml b/src/bundle/Resources/config/services.yaml index 356cf98..b6fe57a 100644 --- a/src/bundle/Resources/config/services.yaml +++ b/src/bundle/Resources/config/services.yaml @@ -1,4 +1,4 @@ imports: - - { resource: services/ezplatform.yaml } + - { resource: services/field_type.yaml } - { resource: services/graphql.yaml } - { resource: services/services.yaml } diff --git a/src/bundle/Resources/config/services/ezplatform.yaml b/src/bundle/Resources/config/services/field_type.yaml similarity index 100% rename from src/bundle/Resources/config/services/ezplatform.yaml rename to src/bundle/Resources/config/services/field_type.yaml diff --git a/src/contracts/QueryFieldLocationService.php b/src/contracts/QueryFieldLocationService.php index 96a6ba5..f84173a 100644 --- a/src/contracts/QueryFieldLocationService.php +++ b/src/contracts/QueryFieldLocationService.php @@ -4,6 +4,7 @@ * @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\Contracts\FieldTypeQuery; @@ -26,7 +27,12 @@ public function loadContentItemsForLocation(Location $location, string $fieldDef * * @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ - public function loadContentItemsSliceForLocation(Location $location, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable; + public function loadContentItemsSliceForLocation( + Location $location, + string $fieldDefinitionIdentifier, + int $offset, + int $limit + ): iterable; /** * Counts the results for the given location. diff --git a/src/contracts/QueryFieldServiceInterface.php b/src/contracts/QueryFieldServiceInterface.php index f26f5a6..1b29958 100644 --- a/src/contracts/QueryFieldServiceInterface.php +++ b/src/contracts/QueryFieldServiceInterface.php @@ -4,6 +4,7 @@ * @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\Contracts\FieldTypeQuery; @@ -37,11 +38,14 @@ public function countContentItems(Content $content, string $fieldDefinitionIdent * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable; + public function loadContentItemsSlice( + Content $content, + string $fieldDefinitionIdentifier, + int $offset, + int $limit + ): iterable; /** - * @return int The page size, or 0 if pagination is disabled. - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function getPaginationConfiguration(Content $content, string $fieldDefinitionIdentifier): int; diff --git a/src/lib/ContentView/FieldDefinitionIdentifierMatcher.php b/src/lib/ContentView/FieldDefinitionIdentifierMatcher.php index feda515..8635b67 100644 --- a/src/lib/ContentView/FieldDefinitionIdentifierMatcher.php +++ b/src/lib/ContentView/FieldDefinitionIdentifierMatcher.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\ContentView; @@ -17,9 +18,9 @@ final class FieldDefinitionIdentifierMatcher extends MultipleValued { /** - * {@inheritdoc} + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function matchLocation(Location $location) + public function matchLocation(Location $location): bool { $contentType = $this->repository ->getContentTypeService() @@ -29,9 +30,9 @@ public function matchLocation(Location $location) } /** - * {@inheritdoc} + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function matchContentInfo(ContentInfo $contentInfo) + public function matchContentInfo(ContentInfo $contentInfo): bool { $contentType = $this->repository ->getContentTypeService() @@ -40,11 +41,6 @@ public function matchContentInfo(ContentInfo $contentInfo) return $this->hasFieldDefinition($contentType); } - /** - * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * - * @return bool - */ private function hasFieldDefinition(ContentType $contentType): bool { foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { @@ -57,20 +53,19 @@ private function hasFieldDefinition(ContentType $contentType): bool } /** - * @param \Ibexa\Core\MVC\Symfony\View\View $view - * - * @return bool - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ - public function match(View $view) + public function match(View $view): bool { if (!$view instanceof ContentValueView) { return false; } + $contentType = $this->repository ->getContentTypeService() - ->loadContentType($view->getContent()->contentInfo->contentTypeId); + ->loadContentType( + $view->getContent()->getContentInfo()->getContentType()->id + ); if (!$this->hasFieldDefinition($contentType)) { return false; @@ -80,6 +75,10 @@ public function match(View $view) return false; } - return in_array($view->getParameter('fieldIdentifier'), $this->getValues(), true); + return in_array( + $view->getParameter('fieldIdentifier'), + $this->getValues(), + true + ); } } diff --git a/src/lib/ContentView/QueryResultsInjector.php b/src/lib/ContentView/QueryResultsInjector.php index 2ec9127..eef386e 100644 --- a/src/lib/ContentView/QueryResultsInjector.php +++ b/src/lib/ContentView/QueryResultsInjector.php @@ -4,10 +4,11 @@ * @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\FieldTypeQuery\ContentView; -use Ibexa\Contracts\FieldTypeQuery\QueryFieldLocationService; +use Exception; use Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Ibexa\Core\MVC\Symfony\View\ContentValueView; @@ -18,24 +19,20 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RequestStack; -final class QueryResultsInjector implements EventSubscriberInterface +final readonly class QueryResultsInjector implements EventSubscriberInterface { - /** @var \Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface&\Ibexa\Contracts\FieldTypeQuery\QueryFieldLocationService */ - private QueryFieldServiceInterface $queryFieldService; - - private array $views; - - private RequestStack $requestStack; - - public function __construct(QueryFieldServiceInterface $queryFieldService, array $views, RequestStack $requestStack) - { + /** + * @param \Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface&\Ibexa\Contracts\FieldTypeQuery\QueryFieldLocationService $queryFieldService + * @param array $views + */ + public function __construct( + private QueryFieldServiceInterface $queryFieldService, + private array $views, + private RequestStack $requestStack + ) { if (!isset($views['item']) || !isset($views['field'])) { throw new \InvalidArgumentException("Both 'item' and 'field' views must be provided"); } - - $this->queryFieldService = $queryFieldService; - $this->views = $views; - $this->requestStack = $requestStack; } public static function getSubscribedEvents(): array @@ -44,7 +41,7 @@ public static function getSubscribedEvents(): array } /** - * {@inheritdoc} + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function injectQueryResults(FilterViewParametersEvent $event): void { @@ -67,11 +64,10 @@ public function injectQueryResults(FilterViewParametersEvent $event): void } /** - * @param \Ibexa\Core\MVC\Symfony\View\Event\FilterViewParametersEvent $event - * * @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Content> * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Exception */ private function buildResults(FilterViewParametersEvent $event): iterable { @@ -80,12 +76,16 @@ private function buildResults(FilterViewParametersEvent $event): iterable $content = $view instanceof ContentValueView ? $view->getContent() : null; if ($location === null && $content === null) { - throw new \Exception('No content nor location to get query results for'); + throw new Exception('No content nor location to get query results for'); } $viewParameters = $event->getBuilderParameters(); $fieldDefinitionIdentifier = $viewParameters['queryFieldDefinitionIdentifier']; - $paginationLimit = $this->queryFieldService->getPaginationConfiguration($content ?? $location->getContent(), $fieldDefinitionIdentifier); + $paginationLimit = $this->queryFieldService->getPaginationConfiguration( + $content ?? $location->getContent(), + $fieldDefinitionIdentifier + ); + $enablePagination = ($viewParameters['enablePagination'] === true); $disablePagination = ($viewParameters['disablePagination'] === true); @@ -138,7 +138,7 @@ private function buildResults(FilterViewParametersEvent $event): iterable return $pager; } else { - if ($this->queryFieldService instanceof QueryFieldLocationService && $location !== null) { + if ($location !== null) { return $this->queryFieldService->loadContentItemsForLocation( $location, $fieldDefinitionIdentifier @@ -149,7 +149,7 @@ private function buildResults(FilterViewParametersEvent $event): iterable $fieldDefinitionIdentifier ); } else { - throw new \Exception('No content nor location to get query results for'); + throw new Exception('No content nor location to get query results for'); } } } diff --git a/src/lib/ContentView/QueryResultsPagerFantaAdapter.php b/src/lib/ContentView/QueryResultsPagerFantaAdapter.php index 42f89e5..2e22f43 100644 --- a/src/lib/ContentView/QueryResultsPagerFantaAdapter.php +++ b/src/lib/ContentView/QueryResultsPagerFantaAdapter.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\ContentView; @@ -12,24 +13,15 @@ use Pagerfanta\Adapter\AdapterInterface; /** - * @implements AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ -final class QueryResultsPagerFantaAdapter implements AdapterInterface +final readonly class QueryResultsPagerFantaAdapter implements AdapterInterface { - private QueryFieldServiceInterface $queryFieldService; - - private Content $content; - - private string $fieldDefinitionIdentifier; - public function __construct( - QueryFieldServiceInterface $queryFieldService, - Content $content, - string $fieldDefinitionIdentifier + private QueryFieldServiceInterface $queryFieldService, + private Content $content, + private string $fieldDefinitionIdentifier ) { - $this->queryFieldService = $queryFieldService; - $this->content = $content; - $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; } public function getNbResults(): int @@ -40,6 +32,9 @@ public function getNbResults(): int ), 0); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function getSlice($offset, $length): iterable { return $this->queryFieldService->loadContentItemsSlice( diff --git a/src/lib/ContentView/QueryResultsWithLocationPagerFantaAdapter.php b/src/lib/ContentView/QueryResultsWithLocationPagerFantaAdapter.php index 39e8084..4a4651d 100644 --- a/src/lib/ContentView/QueryResultsWithLocationPagerFantaAdapter.php +++ b/src/lib/ContentView/QueryResultsWithLocationPagerFantaAdapter.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\ContentView; @@ -12,24 +13,15 @@ use Pagerfanta\Adapter\AdapterInterface; /** - * @implements AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * @implements \Pagerfanta\Adapter\AdapterInterface<\Ibexa\Contracts\Core\Repository\Values\Content\Content> */ -final class QueryResultsWithLocationPagerFantaAdapter implements AdapterInterface +final readonly class QueryResultsWithLocationPagerFantaAdapter implements AdapterInterface { - private QueryFieldLocationService $queryFieldService; - - private Location $location; - - private string $fieldDefinitionIdentifier; - public function __construct( - QueryFieldLocationService $queryFieldService, - Location $location, - string $fieldDefinitionIdentifier + private QueryFieldLocationService $queryFieldService, + private Location $location, + private string $fieldDefinitionIdentifier ) { - $this->queryFieldService = $queryFieldService; - $this->location = $location; - $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; } public function getNbResults(): int diff --git a/src/lib/ExceptionSafeQueryFieldService.php b/src/lib/ExceptionSafeQueryFieldService.php index 612601f..0bdce26 100644 --- a/src/lib/ExceptionSafeQueryFieldService.php +++ b/src/lib/ExceptionSafeQueryFieldService.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery; @@ -15,6 +16,7 @@ use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; +use Throwable; /** * Silences exceptions when they occur in query field service, for example due to field type misconfigurations. @@ -23,12 +25,13 @@ final class ExceptionSafeQueryFieldService implements QueryFieldServiceInterface { use LoggerAwareTrait; - /** @var \Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface&\Ibexa\Contracts\FieldTypeQuery\QueryFieldLocationService */ - private QueryFieldServiceInterface $inner; - - public function __construct(QueryFieldServiceInterface $inner, ?LoggerInterface $logger = null) - { - $this->inner = $inner; + /** + * @param \Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface&\Ibexa\Contracts\FieldTypeQuery\QueryFieldLocationService $inner + */ + public function __construct( + private readonly QueryFieldServiceInterface $inner, + ?LoggerInterface $logger = null + ) { $this->logger = $logger ?: new NullLogger(); } @@ -36,7 +39,7 @@ public function loadContentItems(Content $content, string $fieldDefinitionIdenti { try { return $this->inner->loadContentItems($content, $fieldDefinitionIdentifier); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); @@ -49,7 +52,7 @@ public function countContentItems(Content $content, string $fieldDefinitionIdent { try { return $this->inner->countContentItems($content, $fieldDefinitionIdentifier); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); @@ -58,11 +61,15 @@ public function countContentItems(Content $content, string $fieldDefinitionIdent } } - public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable - { + public function loadContentItemsSlice( + Content $content, + string $fieldDefinitionIdentifier, + int $offset, + int $limit + ): iterable { try { return $this->inner->loadContentItemsSlice($content, $fieldDefinitionIdentifier, $offset, $limit); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); @@ -80,7 +87,7 @@ public function loadContentItemsForLocation(Location $location, string $fieldDef { try { return $this->inner->loadContentItemsForLocation($location, $fieldDefinitionIdentifier); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); @@ -89,11 +96,15 @@ public function loadContentItemsForLocation(Location $location, string $fieldDef } } - public function loadContentItemsSliceForLocation(Location $location, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable - { + public function loadContentItemsSliceForLocation( + Location $location, + string $fieldDefinitionIdentifier, + int $offset, + int $limit + ): iterable { try { return $this->inner->loadContentItemsSliceForLocation($location, $fieldDefinitionIdentifier, $offset, $limit); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); @@ -106,7 +117,7 @@ public function countContentItemsForLocation(Location $location, string $fieldDe { try { return $this->inner->countContentItemsForLocation($location, $fieldDefinitionIdentifier); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); diff --git a/src/lib/FieldType/Form/QueryFieldFormType.php b/src/lib/FieldType/Form/QueryFieldFormType.php index d3f8849..68d0a97 100644 --- a/src/lib/FieldType/Form/QueryFieldFormType.php +++ b/src/lib/FieldType/Form/QueryFieldFormType.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\FieldType\Form; @@ -16,13 +17,10 @@ /** * @phpstan-extends \Symfony\Component\Form\AbstractType */ -class QueryFieldFormType extends AbstractType +final class QueryFieldFormType extends AbstractType { - private FieldTypeService $fieldTypeService; - - public function __construct(FieldTypeService $fieldTypeService) + public function __construct(private readonly FieldTypeService $fieldTypeService) { - $this->fieldTypeService = $fieldTypeService; } public function getName(): string @@ -35,13 +33,17 @@ public function getBlockPrefix(): string return 'ezplatform_fieldtype_query'; } - public function getParent(): ?string + public function getParent(): string { return TextType::class; } public function buildForm(FormBuilderInterface $builder, array $options): void { - $builder->addModelTransformer(new FieldValueTransformer($this->fieldTypeService->getFieldType('ibexa_content_query'))); + $builder->addModelTransformer( + new FieldValueTransformer( + $this->fieldTypeService->getFieldType('ibexa_content_query') + ) + ); } } diff --git a/src/lib/FieldType/Mapper/ParametersTransformer.php b/src/lib/FieldType/Mapper/ParametersTransformer.php index c302e21..18126f3 100644 --- a/src/lib/FieldType/Mapper/ParametersTransformer.php +++ b/src/lib/FieldType/Mapper/ParametersTransformer.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\FieldType\Mapper; @@ -11,20 +12,20 @@ use Symfony\Component\Yaml\Yaml; /** - * @implements DataTransformerInterface + * @implements \Symfony\Component\Form\DataTransformerInterface */ final class ParametersTransformer implements DataTransformerInterface { - public function transform($value): ?string + public function transform(mixed $value): ?string { if ($value === null) { return null; } - return Yaml::dump($value, 2, 4); + return Yaml::dump($value); } - public function reverseTransform($value): mixed + public function reverseTransform(mixed $value): mixed { if ($value === null) { return null; diff --git a/src/lib/FieldType/Mapper/QueryFormMapper.php b/src/lib/FieldType/Mapper/QueryFormMapper.php index 4f88cd7..6d7768d 100644 --- a/src/lib/FieldType/Mapper/QueryFormMapper.php +++ b/src/lib/FieldType/Mapper/QueryFormMapper.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\FieldType\Mapper; @@ -14,31 +15,24 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -final class QueryFormMapper implements FieldDefinitionFormMapperInterface +final readonly class QueryFormMapper implements FieldDefinitionFormMapperInterface { - private ContentTypeService $contentTypeService; - - /** - * List of query types. - * - * @var array - */ - private array $queryTypes; - /** * @param array $queryTypes */ - public function __construct(ContentTypeService $contentTypeService, array $queryTypes = []) - { - $this->contentTypeService = $contentTypeService; - $this->queryTypes = $queryTypes; + public function __construct( + private ContentTypeService $contentTypeService, + private array $queryTypes = [] + ) { } /** * @param \Symfony\Component\Form\FormInterface $fieldDefinitionForm */ - public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data): void - { + public function mapFieldDefinitionForm( + FormInterface $fieldDefinitionForm, + FieldDefinitionData $data + ): void { $parametersForm = $fieldDefinitionForm->getConfig()->getFormFactory()->createBuilder() ->create( 'Parameters', diff --git a/src/lib/FieldType/Query/Type.php b/src/lib/FieldType/Query/Type.php index 44e7e6f..40cba3f 100644 --- a/src/lib/FieldType/Query/Type.php +++ b/src/lib/FieldType/Query/Type.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\FieldType\Query; @@ -17,6 +18,7 @@ use Ibexa\Core\FieldType\ValidationError; use Ibexa\Core\FieldType\Value as BaseValue; use Ibexa\Core\QueryType\QueryTypeRegistry; +use Ibexa\FieldTypeQuery\FieldType\Query\Value as FieldTypeQueryValue; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Translation\TranslationContainerInterface; @@ -32,32 +34,24 @@ final class Type extends FieldType implements TranslationContainerInterface 'ItemsPerPage' => ['type' => 'integer', 'default' => 10], ]; - private QueryTypeRegistry $queryTypeRegistry; - - private ContentTypeService $contentTypeService; - - private string $identifier; - - public function __construct(QueryTypeRegistry $queryTypeRegistry, ContentTypeService $contentTypeService, string $identifier) - { - $this->queryTypeRegistry = $queryTypeRegistry; - $this->contentTypeService = $contentTypeService; - $this->identifier = $identifier; + public function __construct( + private readonly QueryTypeRegistry $queryTypeRegistry, + private readonly ContentTypeService $contentTypeService, + private readonly string $identifier + ) { } - public function validateValidatorConfiguration($validatorConfiguration) + public function validateValidatorConfiguration($validatorConfiguration): array { - $validationErrors = []; - - return $validationErrors; + return []; } - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array { return []; } - public function getFieldTypeIdentifier() + public function getFieldTypeIdentifier(): string { return $this->identifier; } @@ -70,9 +64,6 @@ public function getName(SPIValue $value, FieldDefinition $fieldDefinition, strin return (string)$value->text; } - /** - * @return \Ibexa\FieldTypeQuery\FieldType\Query\Value - */ public function getEmptyValue(): BaseValue { return new Value(); @@ -83,7 +74,7 @@ public function isEmptyValue(SPIValue $value): bool return false; } - protected function createValueFromInput($inputValue) + protected function createValueFromInput($inputValue): mixed { if (is_string($inputValue)) { $inputValue = new Value($inputValue); @@ -115,17 +106,16 @@ protected function checkValueStructure(BaseValue $value): void */ protected function getSortInfo(BaseValue $value) { - return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); + return $this->transformationProcessor->transformByGroup( + (string)$value, + 'lowercase' + ); } /** * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \Ibexa\FieldTypeQuery\FieldType\Query\Value $value */ - public function fromHash($hash) + public function fromHash(mixed $hash): FieldTypeQueryValue|BaseValue { if ($hash === null) { return $this->getEmptyValue(); @@ -138,10 +128,8 @@ public function fromHash($hash) * Converts a $Value to a hash. * * @param \Ibexa\Core\FieldType\TextLine\Value $value - * - * @return mixed */ - public function toHash(SPIValue $value) + public function toHash(SPIValue $value): mixed { if ($this->isEmptyValue($value)) { return null; @@ -150,17 +138,12 @@ public function toHash(SPIValue $value) return $value->text; } - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() + public function isSearchable(): false { return false; } - public function validateFieldSettings($fieldSettings) + public function validateFieldSettings($fieldSettings): array { $errors = []; diff --git a/src/lib/FieldType/Query/Value.php b/src/lib/FieldType/Query/Value.php index 6608a4a..d6622ff 100644 --- a/src/lib/FieldType/Query/Value.php +++ b/src/lib/FieldType/Query/Value.php @@ -4,35 +4,21 @@ * @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\FieldTypeQuery\FieldType\Query; use Ibexa\Core\FieldType\Value as BaseValue; -class Value extends BaseValue +final class Value extends BaseValue { - /** - * Text content. - * - * @var string - */ - public $text; - - /** - * Construct a new Value object and initialize it $text. - * - * @param string $text - */ - public function __construct($text = '') + public function __construct(public string $text = '') { - $this->text = $text; + parent::__construct(); } - /** - * @see \Ibexa\Core\FieldType\Value - */ public function __toString(): string { - return (string)$this->text; + return $this->text; } } diff --git a/src/lib/GraphQL/ContentQueryFieldDefinitionMapper.php b/src/lib/GraphQL/ContentQueryFieldDefinitionMapper.php index 3987316..5b811dd 100644 --- a/src/lib/GraphQL/ContentQueryFieldDefinitionMapper.php +++ b/src/lib/GraphQL/ContentQueryFieldDefinitionMapper.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\GraphQL; diff --git a/src/lib/GraphQL/QueryFieldResolver.php b/src/lib/GraphQL/QueryFieldResolver.php index 061c1e3..4777771 100644 --- a/src/lib/GraphQL/QueryFieldResolver.php +++ b/src/lib/GraphQL/QueryFieldResolver.php @@ -4,26 +4,28 @@ * @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\FieldTypeQuery\GraphQL; +use GraphQL\Executor\Promise\Promise; use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\FieldTypeQuery\QueryFieldServiceInterface; use Ibexa\GraphQL\Value\Field; use Overblog\GraphQLBundle\Definition\Argument; +use Overblog\GraphQLBundle\Relay\Connection\Output\Connection; use Overblog\GraphQLBundle\Relay\Connection\Paginator; -final class QueryFieldResolver +final readonly class QueryFieldResolver { - private QueryFieldServiceInterface $queryFieldService; - - public function __construct(QueryFieldServiceInterface $queryFieldService) + public function __construct(private QueryFieldServiceInterface $queryFieldService) { - $this->queryFieldService = $queryFieldService; } /** * @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Content> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function resolveQueryField(Field $field, Content $content): iterable { @@ -33,7 +35,7 @@ public function resolveQueryField(Field $field, Content $content): iterable /** * @return \GraphQL\Executor\Promise\Promise|\Overblog\GraphQLBundle\Relay\Connection\Output\Connection<\Ibexa\Contracts\Core\Repository\Values\Content\Content>|null */ - public function resolveQueryFieldConnection(Argument $args, ?Field $field, Content $content) + public function resolveQueryFieldConnection(Argument $args, ?Field $field, Content $content): Promise|Connection|null { if ($field === null) { return null; @@ -49,7 +51,7 @@ public function resolveQueryFieldConnection(Argument $args, ?Field $field, Conte return $paginator->auto( $args, - function () use ($content, $field) { + function () use ($content, $field): int { return $this->queryFieldService->countContentItems($content, $field->fieldDefIdentifier); } ); @@ -63,7 +65,6 @@ function () use ($content, $field) { public function resolveQueryFieldDefinitionParameters(array $parameters): array { $return = []; - foreach ($parameters as $name => $value) { $return[] = ['name' => $name, 'value' => $value]; } diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php index 13f97c6..eb47b29 100644 --- a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/QueryConverter.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery\Persistence\Legacy\Content\FieldValue\Converter; @@ -13,14 +14,8 @@ use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; -class QueryConverter implements Converter +final readonly class QueryConverter implements Converter { - /** - * Converts data from $value to $storageFieldValue. - * - * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value - * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue): void { $storageFieldValue->dataText = $value->data; @@ -53,7 +48,7 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin ]; } - public function getIndexColumn() + public function getIndexColumn(): string { return 'sort_key_string'; } diff --git a/src/lib/QueryFieldService.php b/src/lib/QueryFieldService.php index 474c306..2aaedba 100644 --- a/src/lib/QueryFieldService.php +++ b/src/lib/QueryFieldService.php @@ -4,6 +4,7 @@ * @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\FieldTypeQuery; @@ -24,27 +25,18 @@ /** * Executes a query and returns the results. */ -final class QueryFieldService implements QueryFieldServiceInterface, QueryFieldLocationService +final readonly class QueryFieldService implements QueryFieldServiceInterface, QueryFieldLocationService { - private QueryTypeRegistry $queryTypeRegistry; - - private SearchService $searchService; - - private ContentTypeService $contentTypeService; - public function __construct( - SearchService $searchService, - ContentTypeService $contentTypeService, - QueryTypeRegistry $queryTypeRegistry + private SearchService $searchService, + private ContentTypeService $contentTypeService, + private QueryTypeRegistry $queryTypeRegistry ) { - $this->searchService = $searchService; - $this->contentTypeService = $contentTypeService; - $this->queryTypeRegistry = $queryTypeRegistry; } public function loadContentItems(Content $content, string $fieldDefinitionIdentifier): iterable { - $mainLocation = $content->contentInfo->getMainLocation(); + $mainLocation = $content->getContentInfo()->getMainLocation(); if ($mainLocation === null) { return []; } @@ -53,6 +45,9 @@ public function loadContentItems(Content $content, string $fieldDefinitionIdenti return $this->executeQueryAndMapResult($query); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function loadContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): iterable { $query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier); @@ -62,7 +57,7 @@ public function loadContentItemsForLocation(Location $location, string $fieldDef public function countContentItems(Content $content, string $fieldDefinitionIdentifier): int { - $mainLocation = $content->contentInfo->getMainLocation(); + $mainLocation = $content->getContentInfo()->getMainLocation(); if ($mainLocation === null) { return 0; } @@ -74,6 +69,9 @@ public function countContentItems(Content $content, string $fieldDefinitionIdent return $count < 0 ? 0 : $count; } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function countContentItemsForLocation(Location $location, string $fieldDefinitionIdentifier): int { $query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier); @@ -86,7 +84,7 @@ public function countContentItemsForLocation(Location $location, string $fieldDe public function loadContentItemsSlice(Content $content, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable { - $mainLocation = $content->contentInfo->getMainLocation(); + $mainLocation = $content->getContentInfo()->getMainLocation(); if ($mainLocation === null) { return []; } @@ -97,8 +95,15 @@ public function loadContentItemsSlice(Content $content, string $fieldDefinitionI return $this->executeQueryAndMapResult($query); } - public function loadContentItemsSliceForLocation(Location $location, string $fieldDefinitionIdentifier, int $offset, int $limit): iterable - { + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function loadContentItemsSliceForLocation( + Location $location, + string $fieldDefinitionIdentifier, + int $offset, + int $limit + ): iterable { $query = $this->prepareQuery($location->getContent(), $location, $fieldDefinitionIdentifier); $query->offset += $offset; $query->limit = $limit; @@ -110,15 +115,18 @@ public function getPaginationConfiguration(Content $content, string $fieldDefini { $fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier); - if ($fieldDefinition->fieldSettings['EnablePagination'] === false) { + if ($fieldDefinition->getFieldSettings()['EnablePagination'] === false) { return 0; } - return $fieldDefinition->fieldSettings['ItemsPerPage']; + return $fieldDefinition->getFieldSettings()['ItemsPerPage']; } /** - * @param array $expressions parameters that may include expressions to be resolved + * @param array $expressions parameters that may include expressions to be resolved + * @param array $variables + * + * @return array */ private function resolveParameters(array $expressions, array $variables): array { @@ -135,9 +143,9 @@ private function resolveParameters(array $expressions, array $variables): array return $expressions; } - private function isExpression($expression): bool + private function isExpression(mixed $expression): bool { - return is_string($expression) && substr($expression, 0, 2) === '@='; + return is_string($expression) && str_starts_with($expression, '@='); } /** @@ -152,21 +160,32 @@ private function resolveExpression(string $expression, array $variables): mixed return (new ExpressionLanguage())->evaluate(substr($expression, 2), $variables); } - private function prepareQuery(Content $content, Location $location, string $fieldDefinitionIdentifier, array $extraParameters = []): Query - { + /** + * @param array $extraParameters + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function prepareQuery( + Content $content, + Location $location, + string $fieldDefinitionIdentifier, + array $extraParameters = [] + ): Query { $fieldDefinition = $this->loadFieldDefinition($content, $fieldDefinitionIdentifier); - $queryType = $this->queryTypeRegistry->getQueryType($fieldDefinition->fieldSettings['QueryType']); + $queryType = $this->queryTypeRegistry->getQueryType($fieldDefinition->getFieldSettings()['QueryType']); + $contentInfo = $content->getContentInfo(); + $parameters = $this->resolveParameters( - $fieldDefinition->fieldSettings['Parameters'], + $fieldDefinition->getFieldSettings()['Parameters'], array_merge( $extraParameters, [ 'content' => $content, - 'contentInfo' => $content->contentInfo, + 'contentInfo' => $contentInfo, 'location' => $location, - 'mainLocation' => $location->id === $content->contentInfo->mainLocationId ? $location : $content->contentInfo->getMainLocation(), - 'returnedType' => $fieldDefinition->fieldSettings['ReturnedType'], + 'mainLocation' => $location->id === $contentInfo->mainLocationId ? $location : $contentInfo->getMainLocation(), + 'returnedType' => $fieldDefinition->getFieldSettings()['ReturnedType'], ] ) ); @@ -179,13 +198,16 @@ private function prepareQuery(Content $content, Location $location, string $fiel */ private function loadFieldDefinition(Content $content, string $fieldDefinitionIdentifier): FieldDefinition { - $contentType = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId); + $contentType = $this->contentTypeService->loadContentType( + $content->getContentInfo()->contentTypeId + ); + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); if ($fieldDefinition === null) { throw new NotFoundException( 'Query field definition', - $contentType->identifier . '/' . $fieldDefinitionIdentifier + $contentType->getIdentifier() . '/' . $fieldDefinitionIdentifier ); }