From fed1a91cd0b5a5887dbbaa98cadddaa7dc8731bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Parafi=C5=84ski?= Date: Wed, 23 Oct 2024 13:15:23 +0200 Subject: [PATCH] IBX-9103: Added RelationType filtering to fetch relations methods (#440) For more details see https://issues.ibexa.co/browse/IBX-9103 and https://github.com/ibexa/core/pull/440 Key changes: * Added RelationType for filtering purposes to fetch relations methods --- src/contracts/Repository/ContentService.php | 25 +++--- .../Decorator/ContentServiceDecorator.php | 33 +++++--- .../Repository/Values/Content/Relation.php | 10 +++ .../Values/Content/RelationType.php | 37 +++++++++ src/lib/Repository/ContentService.php | 48 +++++++---- .../SiteAccessAware/ContentService.php | 28 ++++--- .../Core/Repository/ContentServiceTest.php | 80 +++++++++++++++++++ 7 files changed, 210 insertions(+), 51 deletions(-) create mode 100644 src/contracts/Repository/Values/Content/RelationType.php diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index 99dcc0fc69..8a8cfed0d0 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -19,6 +19,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; use Ibexa\Contracts\Core\Repository\Values\Content\Relation; use Ibexa\Contracts\Core\Repository\Values\Content\RelationList; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationType; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; @@ -419,7 +420,8 @@ public function loadRelations(VersionInfo $versionInfo): iterable; public function loadRelationList( VersionInfo $versionInfo, int $offset = 0, - int $limit = self::DEFAULT_PAGE_SIZE + int $limit = self::DEFAULT_PAGE_SIZE, + ?RelationType $type = null, ): RelationList; /** @@ -428,7 +430,7 @@ public function loadRelationList( * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ - public function countRelations(VersionInfo $versionInfo): int; + public function countRelations(VersionInfo $versionInfo, ?RelationType $type = null): int; /** * Counts all incoming relations for the given content object. @@ -437,7 +439,7 @@ public function countRelations(VersionInfo $versionInfo): int; * * @return int The number of reverse relations ({@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation}) */ - public function countReverseRelations(ContentInfo $contentInfo): int; + public function countReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): int; /** * Loads all incoming relations for a content object. @@ -446,11 +448,9 @@ public function countReverseRelations(ContentInfo $contentInfo): int; * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] */ - public function loadReverseRelations(ContentInfo $contentInfo): iterable; + public function loadReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): iterable; /** * Loads all incoming relations for a content object. @@ -458,14 +458,13 @@ public function loadReverseRelations(ContentInfo $contentInfo): iterable; * The relations come only from published versions of the source content objects. * If the user is not allowed to read specific version then UnauthorizedRelationListItem is returned * {@see \Ibexa\Contracts\Core\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem} - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param int $offset - * @param int $limit - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\RelationList */ - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList; + public function loadReverseRelationList( + ContentInfo $contentInfo, + int $offset = 0, + int $limit = -1, + ?RelationType $type = null + ): RelationList; /** * Adds a relation of type common. diff --git a/src/contracts/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php index 0e21ee7b09..3a65123467 100644 --- a/src/contracts/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -20,6 +20,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; use Ibexa\Contracts\Core\Repository\Values\Content\Relation; use Ibexa\Contracts\Core\Repository\Values\Content\RelationList; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationType; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; @@ -193,29 +194,37 @@ public function loadRelations(VersionInfo $versionInfo): iterable return $this->innerService->loadRelations($versionInfo); } - public function countRelations(VersionInfo $versionInfo): int + public function countRelations(VersionInfo $versionInfo, ?RelationType $type = null): int { - return $this->innerService->countRelations($versionInfo); + return $this->innerService->countRelations($versionInfo, $type); } - public function loadRelationList(VersionInfo $versionInfo, int $offset = 0, int $limit = self::DEFAULT_PAGE_SIZE): RelationList - { - return $this->innerService->loadRelationList($versionInfo, $offset, $limit); + public function loadRelationList( + VersionInfo $versionInfo, + int $offset = 0, + int $limit = self::DEFAULT_PAGE_SIZE, + ?RelationType $type = null + ): RelationList { + return $this->innerService->loadRelationList($versionInfo, $offset, $limit, $type); } - public function countReverseRelations(ContentInfo $contentInfo): int + public function countReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): int { - return $this->innerService->countReverseRelations($contentInfo); + return $this->innerService->countReverseRelations($contentInfo, $type); } - public function loadReverseRelations(ContentInfo $contentInfo): iterable + public function loadReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): iterable { - return $this->innerService->loadReverseRelations($contentInfo); + return $this->innerService->loadReverseRelations($contentInfo, $type); } - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList - { - return $this->innerService->loadReverseRelationList($contentInfo, $offset, $limit); + public function loadReverseRelationList( + ContentInfo $contentInfo, + int $offset = 0, + int $limit = -1, + ?RelationType $type = null + ): RelationList { + return $this->innerService->loadReverseRelationList($contentInfo, $offset, $limit, $type); } public function addRelation( diff --git a/src/contracts/Repository/Values/Content/Relation.php b/src/contracts/Repository/Values/Content/Relation.php index 71376cfff1..0b6c944588 100644 --- a/src/contracts/Repository/Values/Content/Relation.php +++ b/src/contracts/Repository/Values/Content/Relation.php @@ -25,6 +25,8 @@ abstract class Relation extends ValueObject * The relation type COMMON is a general relation between object set by a user. * * @var int + * + * @deprecated 5.0.0 const is deprecated and will be removed in 6.0.0. Use {@see RelationType::COMMON} instead. */ public const COMMON = 1; @@ -32,6 +34,8 @@ abstract class Relation extends ValueObject * the relation type EMBED is set for a relation which is anchored as embedded link in an attribute value. * * @var int + * + * @deprecated 5.0.0 const is deprecated and will be removed in 6.0.0. Use {@see RelationType::EMBED} instead. */ public const EMBED = 2; @@ -39,6 +43,8 @@ abstract class Relation extends ValueObject * the relation type LINK is set for a relation which is anchored as link in an attribute value. * * @var int + * + * @deprecated 5.0.0 const is deprecated and will be removed in 6.0.0. Use {@see RelationType::LINK} instead. */ public const LINK = 4; @@ -46,6 +52,8 @@ abstract class Relation extends ValueObject * the relation type FIELD is set for a relation which is part of an relation attribute value. * * @var int + * + * @deprecated 5.0.0 const is deprecated and will be removed in 6.0.0. Use {@see RelationType::FIELD} instead. */ public const FIELD = 8; @@ -53,6 +61,8 @@ abstract class Relation extends ValueObject * the relation type ASSET is set for a relation to asset in an attribute value. * * @var int + * + * @deprecated 5.0.0 const is deprecated and will be removed in 6.0.0. Use {@see RelationType::ASSET} instead. */ public const ASSET = 16; diff --git a/src/contracts/Repository/Values/Content/RelationType.php b/src/contracts/Repository/Values/Content/RelationType.php new file mode 100644 index 0000000000..40a47fda7a --- /dev/null +++ b/src/contracts/Repository/Values/Content/RelationType.php @@ -0,0 +1,37 @@ +isPublished() ? 'read' : 'versionread'; @@ -2057,12 +2058,17 @@ public function countRelations(APIVersionInfo $versionInfo): int return $this->persistenceHandler->contentHandler()->countRelations( $contentInfo->id, - $versionInfo->versionNo + $versionInfo->versionNo, + $type?->value ); } - public function loadRelationList(APIVersionInfo $versionInfo, int $offset = 0, int $limit = self::DEFAULT_PAGE_SIZE): RelationList - { + public function loadRelationList( + APIVersionInfo $versionInfo, + int $offset = 0, + int $limit = self::DEFAULT_PAGE_SIZE, + ?RelationType $type = null + ): RelationList { $function = $versionInfo->isPublished() ? 'read' : 'versionread'; $list = new RelationList(); @@ -2074,7 +2080,8 @@ public function loadRelationList(APIVersionInfo $versionInfo, int $offset = 0, i $contentInfo = $versionInfo->getContentInfo(); $list->totalCount = $this->persistenceHandler->contentHandler()->countRelations( $contentInfo->id, - $versionInfo->versionNo + $versionInfo->versionNo, + $type?->value ); if ($list->totalCount === 0) { @@ -2086,6 +2093,7 @@ public function loadRelationList(APIVersionInfo $versionInfo, int $offset = 0, i $limit, $offset, $versionInfo->versionNo, + $type?->value ); $destinationContentIds = array_column($persistenceRelationList, 'destinationContentId'); @@ -2120,13 +2128,13 @@ public function loadRelationList(APIVersionInfo $versionInfo, int $offset = 0, i /** * {@inheritdoc} */ - public function countReverseRelations(ContentInfo $contentInfo): int + public function countReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): int { if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { return 0; } - return $this->persistenceHandler->contentHandler()->countReverseRelations($contentInfo->getId()); + return $this->persistenceHandler->contentHandler()->countReverseRelations($contentInfo->getId(), $type?->value); } /** @@ -2136,18 +2144,17 @@ public function countReverseRelations(ContentInfo $contentInfo): int * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] */ - public function loadReverseRelations(ContentInfo $contentInfo): iterable + public function loadReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): iterable { if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { throw new UnauthorizedException('content', 'reverserelatedlist', ['contentId' => $contentInfo->id]); } $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations( - $contentInfo->id + $contentInfo->id, + $type?->value ); $returnArray = []; @@ -2169,22 +2176,33 @@ public function loadReverseRelations(ContentInfo $contentInfo): iterable /** * {@inheritdoc} + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param int $offset + * @param int $limit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\RelationType|null $type */ - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList - { + public function loadReverseRelationList( + ContentInfo $contentInfo, + int $offset = 0, + int $limit = -1, + ?RelationType $type = null + ): RelationList { $list = new RelationList(); if (!$this->repository->getPermissionResolver()->canUser('content', 'reverserelatedlist', $contentInfo)) { return $list; } $list->totalCount = $this->persistenceHandler->contentHandler()->countReverseRelations( - $contentInfo->id + $contentInfo->id, + $type?->value ); if ($list->totalCount > 0) { $spiRelationList = $this->persistenceHandler->contentHandler()->loadReverseRelationList( $contentInfo->id, $offset, - $limit + $limit, + $type?->value ); foreach ($spiRelationList as $spiRelation) { $sourceContentInfo = $this->internalLoadContentInfoById($spiRelation->sourceContentId); diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php index 07fc7893a3..6a7a79277a 100644 --- a/src/lib/Repository/SiteAccessAware/ContentService.php +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -21,6 +21,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; use Ibexa\Contracts\Core\Repository\Values\Content\Relation; use Ibexa\Contracts\Core\Repository\Values\Content\RelationList; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationType; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; @@ -196,35 +197,40 @@ public function loadRelations(VersionInfo $versionInfo): iterable return $this->service->loadRelations($versionInfo); } - public function countRelations(VersionInfo $versionInfo): int + public function countRelations(VersionInfo $versionInfo, ?RelationType $type = null): int { - return $this->service->countRelations($versionInfo); + return $this->service->countRelations($versionInfo, $type); } public function loadRelationList( VersionInfo $versionInfo, int $offset = 0, - int $limit = self::DEFAULT_PAGE_SIZE + int $limit = self::DEFAULT_PAGE_SIZE, + ?RelationType $type = null ): RelationList { - return $this->service->loadRelationList($versionInfo, $offset, $limit); + return $this->service->loadRelationList($versionInfo, $offset, $limit, $type); } /** * {@inheritdoc} */ - public function countReverseRelations(ContentInfo $contentInfo): int + public function countReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): int { - return $this->service->countReverseRelations($contentInfo); + return $this->service->countReverseRelations($contentInfo, $type); } - public function loadReverseRelations(ContentInfo $contentInfo): iterable + public function loadReverseRelations(ContentInfo $contentInfo, ?RelationType $type = null): iterable { - return $this->service->loadReverseRelations($contentInfo); + return $this->service->loadReverseRelations($contentInfo, $type); } - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList - { - return $this->service->loadReverseRelationList($contentInfo, $offset, $limit); + public function loadReverseRelationList( + ContentInfo $contentInfo, + int $offset = 0, + int $limit = -1, + ?RelationType $type = null + ): RelationList { + return $this->service->loadReverseRelationList($contentInfo, $offset, $limit, $type); } public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation diff --git a/tests/integration/Core/Repository/ContentServiceTest.php b/tests/integration/Core/Repository/ContentServiceTest.php index 00b934058a..510a779711 100644 --- a/tests/integration/Core/Repository/ContentServiceTest.php +++ b/tests/integration/Core/Repository/ContentServiceTest.php @@ -22,6 +22,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationType; use Ibexa\Contracts\Core\Repository\Values\Content\Section; use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; @@ -31,6 +32,7 @@ use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; use Ibexa\Contracts\Core\Repository\Values\User\User; use Ibexa\Core\Base\Exceptions\UnauthorizedException as CoreUnauthorizedException; +use Ibexa\Core\FieldType\Relation\Value as RelationValue; use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; use InvalidArgumentException; @@ -3827,6 +3829,21 @@ public function testCountRelationsForUnauthorizedUser(): void self::assertSame(0, $this->contentService->countRelations($draft->getVersionInfo())); } + public function testCountRelationsWithType(): void + { + $draft = $this->createContentDraft('folder', 56, ['name' => 'relation target']); + $content = $this->createContentWithFieldRelation($this->contentService->publishVersion($draft->getVersionInfo())); + + self::assertEquals( + 0, + $this->contentService->countRelations($content->getVersionInfo(), RelationType::ASSET) + ); + self::assertEquals( + 1, + $this->contentService->countRelations($content->getVersionInfo(), RelationType::FIELD) + ); + } + public function testLoadRelationList(): void { $draft = $this->createContentWithRelations(); @@ -3882,6 +3899,22 @@ public function testLoadRelationListWithPagination(): void ); } + public function testLoadRelationListWithType(): void + { + $draft = $this->createContentDraft('folder', 56, ['name' => 'relation target']); + $content = $this->createContentWithFieldRelation($this->contentService->publishVersion($draft->getVersionInfo())); + + $relationList = $this->contentService->loadRelationList($content->getVersionInfo(), 0, 10, RelationType::FIELD); + self::assertCount( + 1, + $relationList->items + ); + self::assertEquals( + $draft->getId(), + $relationList->items[0]->getRelation()?->getDestinationContentInfo()->getId() + ); + } + /** * Test for the countReverseRelations() method. * @@ -3903,6 +3936,22 @@ public function testCountReverseRelations(): void self::assertEquals(2, $this->contentService->countReverseRelations($contentInfo)); } + public function testCountReverseRelationsWithType(): void + { + $draft = $this->createContentDraft('folder', 56, ['name' => 'relation target']); + $relationTargetContent = $this->contentService->publishVersion($draft->getVersionInfo()); + $this->createContentWithFieldRelation($relationTargetContent); + + self::assertEquals( + 0, + $this->contentService->countReverseRelations($relationTargetContent->getContentInfo(), RelationType::ASSET) + ); + self::assertEquals( + 1, + $this->contentService->countReverseRelations($relationTargetContent->getContentInfo(), RelationType::FIELD) + ); + } + /** * Test for the countReverseRelations() method. * @@ -4309,6 +4358,23 @@ public function testLoadReverseRelationListSkipsDraftContent() ); } + public function testLoadReverseRelationListWithType(): void + { + $draft = $this->createContentDraft('folder', 56, ['name' => 'relation target']); + $targetContent = $this->contentService->publishVersion($draft->getVersionInfo()); + $this->createContentWithFieldRelation($targetContent); + + $relationList = $this->contentService->loadReverseRelationList($targetContent->getContentInfo(), 0, 10, RelationType::FIELD); + self::assertCount( + 1, + $relationList->items + ); + self::assertEquals( + $draft->getId(), + $relationList->items[0]->getRelation()?->getDestinationContentInfo()->getId() + ); + } + /** * Test for the deleteRelation() method. * @@ -6897,4 +6963,18 @@ private function createContentWithRelations(): Content return $draft; } + + private function createContentWithFieldRelation(Content $targetContent): Content + { + $draft = $this->createContentDraft( + 'gallery', + 56, + [ + 'name' => 'Content With Single Relation', + 'image' => new RelationValue($targetContent->getId()), + ] + ); + + return $this->contentService->publishVersion($draft->getVersionInfo()); + } }