From 9e313d2e1a0ad44692bcaf55cf2476fe42c84a72 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:38:19 +0100 Subject: [PATCH 1/2] [PHPDoc] Improved ContentService doc for API reference (#379) For more details see https://github.com/ibexa/core/pull/379 Key changes: * [PHPDoc] Aligned ContentService PHPDocs with API reference requirements * [PHPStan] Aligned baseline with the changes-baseline*.neon --------- Co-Authored-By: Konrad Oboza --- phpstan-baseline-7.4.neon | 18 +- phpstan-baseline-gte-8.0.neon | 18 +- phpstan-baseline.neon | 78 ++--- src/contracts/Repository/ContentService.php | 312 ++++++++------------ 4 files changed, 168 insertions(+), 258 deletions(-) diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index 702873d5be..9b8ecc1c33 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -6,17 +6,17 @@ parameters: path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php - - message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php - - message: "#^Parameter \\#1 \\$input of function array_slice expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$input of function array_slice expects array, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php @@ -171,7 +171,7 @@ parameters: path: src/lib/MVC/Symfony/Component/Serializer/AbstractPropertyWhitelistNormalizer.php - - message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\ given\\.$#" count: 1 path: src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php @@ -351,17 +351,17 @@ parameters: path: tests/integration/Core/Repository/BaseTest.php - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" count: 4 path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php - - message: "#^Parameter \\#1 \\$array_arg of function usort expects TArray of array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array_arg of function usort expects TArray of array, iterable\\ given\\.$#" count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\ given\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -381,12 +381,12 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php diff --git a/phpstan-baseline-gte-8.0.neon b/phpstan-baseline-gte-8.0.neon index 51c2930bdc..f4d695d9ca 100644 --- a/phpstan-baseline-gte-8.0.neon +++ b/phpstan-baseline-gte-8.0.neon @@ -6,17 +6,17 @@ parameters: path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php - - message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php - - message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: src/bundle/Core/Command/CleanupVersionsCommand.php @@ -171,7 +171,7 @@ parameters: path: src/lib/MVC/Symfony/Component/Serializer/AbstractPropertyWhitelistNormalizer.php - - message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\ given\\.$#" count: 1 path: src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php @@ -306,12 +306,12 @@ parameters: path: tests/integration/Core/Repository/BaseTest.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 4 path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php - - message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\ given\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -321,7 +321,7 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$array of function usort expects TArray of array, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$array of function usort expects TArray of array, iterable\\ given\\.$#" count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -336,12 +336,12 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d601a47675..7bfaaed552 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -231,7 +231,7 @@ parameters: path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php - - message: "#^Cannot access offset int on iterable\\\\.$#" + message: "#^Cannot access offset int on iterable\\\\.$#" count: 1 path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php @@ -5550,11 +5550,6 @@ parameters: count: 1 path: src/contracts/Persistence/User/Policy.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/ContentService.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -5565,21 +5560,6 @@ parameters: count: 1 path: src/contracts/Repository/ContentService.php - - - message: "#^PHPDoc tag @param for parameter \\$contentId with type mixed is not subtype of native type int\\.$#" - count: 1 - path: src/contracts/Repository/ContentService.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language\\.\\)\\: Unexpected token \"if\", expected variable at offset 870$#" - count: 1 - path: src/contracts/Repository/ContentService.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecorator\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/Decorator/ContentServiceDecorator.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecorator\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -8606,7 +8586,7 @@ parameters: path: src/lib/Base/Utils/DeprecationWarnerInterface.php - - message: "#^Parameter \\#1 \\$locations of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent constructor expects array, array\\|iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$locations of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent constructor expects array, array\\|iterable\\ given\\.$#" count: 1 path: src/lib/Event/ContentService.php @@ -19775,11 +19755,6 @@ parameters: count: 1 path: src/lib/Repository/ContentService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/ContentService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByRemoteId\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" count: 1 @@ -19831,7 +19806,7 @@ parameters: path: src/lib/Repository/ContentService.php - - message: "#^Parameter \\#3 \\$prioritizedLanguages of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectFromPersistence\\(\\) expects array\\, array\\\\|null given\\.$#" + message: "#^Parameter \\#3 \\$prioritizedLanguages of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectFromPersistence\\(\\) expects array\\, array\\\\|null given\\.$#" count: 1 path: src/lib/Repository/ContentService.php @@ -20785,11 +20760,6 @@ parameters: count: 1 path: src/lib/Repository/SettingService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/SiteAccessAware/ContentService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentService\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -27121,52 +27091,62 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 0 on iterable\\\\.$#" + message: "#^Cannot access offset 0 on iterable\\\\.$#" count: 12 path: tests/integration/Core/Repository/ContentServiceTest.php - message: "#^Cannot access offset 0 on iterable\\\\.$#" - count: 11 + count: 7 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\\\.$#" + count: 4 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 1 on iterable\\\\.$#" + message: "#^Cannot access offset 1 on iterable\\\\.$#" count: 4 path: tests/integration/Core/Repository/ContentServiceTest.php - message: "#^Cannot access offset 1 on iterable\\\\.$#" - count: 3 + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 1 on iterable\\\\.$#" + count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 2 on iterable\\\\.$#" + message: "#^Cannot access offset 2 on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 3 on iterable\\\\.$#" + message: "#^Cannot access offset 3 on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 4 on iterable\\\\.$#" + message: "#^Cannot access offset 4 on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 5 on iterable\\\\.$#" + message: "#^Cannot access offset 5 on iterable\\\\.$#" count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset mixed on iterable\\\\.$#" + message: "#^Cannot access offset mixed on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset mixed on iterable\\\\.$#" + message: "#^Cannot access offset int on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -27256,7 +27236,7 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelation\\(\\) should return array\\ but returns iterable\\\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelation\\(\\) should return array\\ but returns iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -27371,7 +27351,7 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithRelations\\(\\) should return array\\ but returns iterable\\\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithRelations\\(\\) should return array\\ but returns iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -27871,7 +27851,7 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersions\\(\\) should return array\\ but returns iterable\\\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersions\\(\\) should return array\\ but returns iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -30726,7 +30706,7 @@ parameters: path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php - - message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\, iterable\\ given\\.$#" count: 5 path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php @@ -30826,7 +30806,7 @@ parameters: path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php - - message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\, iterable\\ given\\.$#" + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\, iterable\\ given\\.$#" count: 5 path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php @@ -56101,7 +56081,7 @@ parameters: path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php - - message: "#^Parameter \\#1 \\$contentIds of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoList\\(\\) expects array\\, array\\ given\\.$#" + message: "#^Parameter \\#1 \\$contentIds of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoList\\(\\) expects array\\, array\\ given\\.$#" count: 1 path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php diff --git a/src/contracts/Repository/ContentService.php b/src/contracts/Repository/ContentService.php index fb3bccb5c9..481bdc9c8e 100644 --- a/src/contracts/Repository/ContentService.php +++ b/src/contracts/Repository/ContentService.php @@ -37,23 +37,19 @@ interface ContentService * * To load fields use loadContent * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given id does not exist - * - * @param int $contentId - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content with the given id doesn't exist. */ public function loadContentInfo(int $contentId): ContentInfo; /** * Bulk-load ContentInfo items by id's. * - * Note: It does not throw exceptions on load, just skips erroneous (NotFound or Unauthorized) ContentInfo items. + * Note: It doesn't throw exceptions on load, just skips erroneous (NotFound or Unauthorized) ContentInfo items. * - * @param int[] $contentIds + * @param array $contentIds * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo[] list of ContentInfo with Content Ids as keys + * @return array List of ContentInfo with content ids as keys */ public function loadContentInfoList(array $contentIds): iterable; @@ -62,49 +58,39 @@ public function loadContentInfoList(array $contentIds): iterable; * * To load fields use loadContent * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist - * - * @param string $remoteId - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content with the given remote id doesn't exist. */ public function loadContentInfoByRemoteId(string $remoteId): ContentInfo; /** * Loads a version info of the given content object. * - * If no version number is given, the method returns the current version + * If no version number is given, the method returns the current version. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the version with the given number does not exist - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the version with the given number doesn't exist. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo * @param int|null $versionNo the version number. If not given the current version is returned. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo */ public function loadVersionInfo(ContentInfo $contentInfo, ?int $versionNo = null): VersionInfo; /** * Loads a version info of the given content object id. * - * If no version number is given, the method returns the current version + * If no version number is given, the method returns the current version. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the version with the given number does not exist - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the version with the given number doesn't exist. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version. * - * @param int $contentId * @param int|null $versionNo the version number. If not given the current version is returned. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo */ public function loadVersionInfoById(int $contentId, ?int $versionNo = null): VersionInfo; /** * Bulk-load VersionInfo items by the list of ContentInfo Value Objects. * - * @param array<\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo> $contentInfoList + * @param array $contentInfoList * * @return array List of VersionInfo items with Content Ids as keys * @@ -119,30 +105,24 @@ public function loadVersionInfoListByContentInfo(array $contentInfoList): array; * * If no version number is given, the method returns the current version * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if version with the given number does not exist - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if version with the given number doesn't exist. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param array $languages A language priority, filters returned fields and is used as prioritized language code on + * @param array $languages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. - * @param int|null $versionNo the version number. If not given the current version is returned from $contentInfo - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param int|null $versionNo The version number. If not given the current version is returned from $contentInfo. + * @param bool $useAlwaysAvailable Add Main language to $languages if true (default) and if {@see ContentInfo::$alwaysAvailable} is true. */ public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; /** * Loads content in the version given by version info. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param bool $useAlwaysAvailable Add Main language to $languages if true (default) and if {@see ContentInfo::$alwaysAvailable} is true. */ public function loadContentByVersionInfo(VersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): Content; @@ -151,16 +131,15 @@ public function loadContentByVersionInfo(VersionInfo $versionInfo, array $langua * * If no version number is given, the method returns the current version * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content or version with the given id and languages doesn't exist. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user lacks: + * - `content/read` permission for published content, or + * - `content/read` and `content/versionread` permissions for draft content. * - * @param mixed $contentId - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * @param array $languages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. - * @param int|null $versionNo the version number. If not given the current version is returned - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param int|null $versionNo The version number. If not given the current version is returned. + * @param bool $useAlwaysAvailable Add Main language to $languages if true (default) and if {@see ContentInfo::$alwaysAvailable} is true. */ public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; @@ -169,33 +148,32 @@ public function loadContent(int $contentId, array $languages = null, ?int $versi * * If no version is given, the method returns the current version * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content or version with the given remote id doesn't exist. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user lacks: + * - `content/read` permission for published content, or + * - `content/read` and `content/versionread` permissions for draft content. * - * @param string $remoteId * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. * @param int|null $versionNo the version number. If not given the current version is returned - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if {@see ContentInfo::$alwaysAvailable} is true. */ public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; /** * Bulk-load Content items by the list of ContentInfo Value Objects. * - * Note: it does not throw exceptions on load, just ignores erroneous Content item. + * Note: it doesn't throw exceptions on load, just ignores erroneous Content item. * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is * allowed to access every Content on the list. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo[] $contentInfoList - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * @param array $contentInfoList + * @param array $languages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true, + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if {@see ContentInfo::$alwaysAvailable} is true, * unless all languages have been asked for. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] list of Content items with Content Ids as keys + * @return array List of Content items with Content Ids as keys */ public function loadContentListByContentInfo(array $contentInfoList, array $languages = [], bool $useAlwaysAvailable = true): iterable; @@ -204,49 +182,43 @@ public function loadContentListByContentInfo(array $contentInfoList, array $lang * * If a different userId is given in $contentCreateStruct it is assigned to the given user * but this required special rights for the authenticated user - * (this is useful for content staging where the transfer process does not + * (this is useful for content staging where the transfer process doesn't * have to authenticate with the user which created the content object in the source server). * The user has to publish the draft if it should be visible. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is a provided remote ID which exists in the system or multiple Locations * are under the same parent or if the a field value is not accepted by the field type - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is missing or is set to an empty value + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is missing or is set to an empty value. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs an array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct} for each location parent under which a location should be created for the content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs An array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct} for each location parent under which a location should be created for the content. * While optional, it's highly recommended to use Locations for content as a lot of features in the system is usually tied to the tree structure (including default Role policies). * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null * for case of full validation. Empty identifiers array is equal to no validation. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content The newly created content draft. */ public function createContent(ContentCreateStruct $contentCreateStruct, array $locationCreateStructs = [], ?array $fieldIdentifiersToValidate = null): Content; /** * Updates the metadata. * - * See {@see ContentMetadataUpdateStruct} of a content object - to update fields use updateContent + * To update fields, use {@see ContentService::updateContent()}. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content metadata. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content with the updated attributes + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content The content with the updated attributes. */ public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): Content; /** * Deletes a content object including all its versions and locations including their subtrees. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object) - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object). * - * @return int[] Affected Location Id's (List of Locations of the Content that was deleted) + * @return array Affected Location IDs (List of Location IDs of the Content that was deleted). */ public function deleteContent(ContentInfo $contentInfo): iterable; @@ -257,14 +229,12 @@ public function deleteContent(ContentInfo $contentInfo): iterable; * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language. * It can be changed on updating the version. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current user is not allowed to create the draft. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null $versionInfo - * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $creator Used as creator of the draft if given - otherwise uses current-user - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language. + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $creator Used as creator of the draft if given; otherwise uses current user. + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language|null $language If not set the draft is created with the initialLanguage code of the source version or if not present with the main language. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content The newly created content draft. */ public function createContentDraft( ContentInfo $contentInfo, @@ -276,11 +246,11 @@ public function createContentDraft( /** * Counts drafts for a user. * - * If no user is given the number of drafts for the authenticated user are returned + * If no user is given the number of drafts for the authenticated user are returned. * - * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current user. * - * @return int The number of drafts ({@see VersionInfo}) owned by the given user + * @return int The number of drafts ({@see VersionInfo}) owned by the given user. * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ @@ -289,48 +259,42 @@ public function countContentDrafts(?User $user = null): int; /** * Loads drafts for a user. * - * If no user is given the drafts for the authenticated user are returned + * If no user is given the drafts for the authenticated user are returned. * * @deprecated Please use {@see ContentService::loadContentDraftList()} instead to avoid risking loading too much data. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current user is not allowed to load the draft list * - * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user The user to load drafts for. If `null`, the current user's drafts are loaded. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] the drafts ({@see VersionInfo}) owned by the given user + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] The drafts owned by the given user. */ public function loadContentDrafts(?User $user = null): iterable; /** * Loads drafts for a user when content is not in the trash. The list is sorted by modification date. * - * If no user is given the drafts for the authenticated user are returned + * If no user is given the drafts for the authenticated user are returned. * * @since 7.5.5 * - * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user The user to load drafts for, if defined, otherwise drafts for current-user - * @param int $offset - * @param int $limit - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentDraftList + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user The user to load drafts for, if defined; otherwise drafts for current user. */ public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList; /** * Updates the fields of a draft. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentUpdateStruct is not valid - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is set to an empty value - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentUpdateStruct is not valid. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is set to an empty value. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct - * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null + * @param array|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null * for case of full validation. Empty identifiers array is equal to no validation. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content draft with the updated fields + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content The content draft with the updated fields. */ public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): Content; @@ -342,18 +306,15 @@ public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $con * * @todo Introduce null|int ContentType->versionArchiveLimit to be able to let admins override this per type. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo - * @param string[] $translations List of language codes of translations which will be included + * @param array $translations List of language codes of translations which will be included * in a published version. - * By default all translations from the current version will be published. - * If the list is provided but does not cover all currently published translations, + * By default, all translations from the current version will be published. + * If the list is provided but doesn't cover all currently published translations, * the missing ones will be copied from the currently published version, * overriding those in the current version. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content; @@ -361,23 +322,18 @@ public function publishVersion(VersionInfo $versionInfo, array $translations = L * Removes the given version. * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is in - * published state or is a last version of Content in non draft state - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * published state or is a last version of Content in non-draft state. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version. */ public function deleteVersion(VersionInfo $versionInfo): void; /** * Loads all versions for the given content. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the given status is invalid - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param int|null $status + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the given status is invalid. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] an array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo} sorted by creation date + * @return array An array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo} sorted by creation date. */ public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable; @@ -385,24 +341,20 @@ public function loadVersions(ContentInfo $contentInfo, ?int $status = null): ite * Copies the content to a new location. If no version is given, * all versions are copied, otherwise only the given version. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct The target location where the content is copied to. */ public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?VersionInfo $versionInfo = null): Content; /** * Loads all outgoing relations for the given version. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version. * - * @deprecated 4.5.7 The "ContentService::loadRelations()" method is deprecated, will be removed in 5.0. + * @deprecated 4.5.7 It will be removed in 5.0. Use {@see ContentService::loadRelationList()} instead. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + * @return array */ public function loadRelations(VersionInfo $versionInfo): iterable; @@ -433,9 +385,7 @@ public function countRelations(VersionInfo $versionInfo): int; /** * Counts all incoming relations for the given content object. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * - * @return int The number of reverse relations ({@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation}) + * @return int The number of reverse relations ({@see Relation}). */ public function countReverseRelations(ContentInfo $contentInfo): int; @@ -444,11 +394,9 @@ public function countReverseRelations(ContentInfo $contentInfo): int; * * The relations come only from published versions of the source content objects * - * @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 + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + * @return array */ public function loadReverseRelations(ContentInfo $contentInfo): iterable; @@ -456,42 +404,36 @@ public function loadReverseRelations(ContentInfo $contentInfo): iterable; * Loads all incoming relations for a content object. * * 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 + * If the user is not allowed to read specific version then {@see \Ibexa\Contracts\Core\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem} is returned */ public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList; /** - * Adds a relation of type common. + * Adds a common relation. * * The source of the relation is the content and version - * referenced by $versionInfo. + * referenced by $sourceVersion. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion The source content's version in relation with the destination. + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent The destination of the relation. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation The newly created relation. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation the newly created relation + * @see Relation::COMMON */ public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation; /** - * Removes a relation of type COMMON from a draft. + * Removes a common relation from a draft. * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is no relation of type {@see Relation::COMMON} for the given destination. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent + * @see Relation::COMMON */ public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): void; @@ -504,12 +446,9 @@ public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinat * is the Main Translation of a Content Item. * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed * to delete the content (in one of the locations of the given Content Item). - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if $languageCode argument * is invalid for the given content. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo - * @param string $languageCode - * * @since 6.13 */ public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void; @@ -517,7 +456,7 @@ public function deleteTranslation(ContentInfo $contentInfo, string $languageCode /** * Delete specified Translation from a Content Draft. * - * When using together with ContentService::publishVersion() method, make sure to not provide deleted translation + * When using together with {@see ContentService::publishVersion()} method, make sure to not provide deleted translation * in translations array, as it is going to be copied again from published version. * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the specified Translation @@ -526,12 +465,12 @@ public function deleteTranslation(ContentInfo $contentInfo, string $languageCode * to edit the Content (in one of the locations of the given Content Object). * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument * is invalid for the given Draft. - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if specified Version was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if specified Version was not found. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft - * @param string $languageCode Language code of the Translation to be removed + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft. + * @param string $languageCode Language code of the Translation to be removed. * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft w/o the specified Translation + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft without the specified Translation. * * @since 6.12 */ @@ -539,49 +478,38 @@ public function deleteTranslationFromDraft(VersionInfo $versionInfo, string $lan /** * Hides Content by making all the Locations appear hidden. - * It does not persist hidden state on Location object itself. * - * Content hidden by this API can be revealed by revealContent API. + * It doesn't persist hidden state on Location object itself. * - * @see ContentService::revealContent() + * Content hidden by this API can be revealed by {@see ContentService::revealContent()} API. * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @see ContentService::revealContent() */ public function hideContent(ContentInfo $contentInfo): void; /** * Reveals Content hidden by hideContent API. + * * Locations which were hidden before hiding Content will remain hidden. * * @see ContentService::hideContent() - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ public function revealContent(ContentInfo $contentInfo): void; /** * Instantiates a new content create struct object. * - * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable - * - * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * @param string $mainLanguageCode - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct + * {@see ContentCreateStruct::$alwaysAvailable} is set to the {@see ContentType::$defaultAlwaysAvailable}. */ public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): ContentCreateStruct; /** * Instantiates a new content meta data update struct. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct */ public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct; /** * Instantiates a new content update struct. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct */ public function newContentUpdateStruct(): ContentUpdateStruct; @@ -589,7 +517,7 @@ public function newContentUpdateStruct(): ContentUpdateStruct; * Validates given content related ValueObject returning field errors structure as a result. * * @param array $context Additional context parameters to be used by validators. - * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null + * @param array|null $fieldIdentifiersToValidate List of field identifiers for partial validation, or null * for case of full validation. Empty identifiers array is equal to no validation. * * @return array Validation errors grouped by field definition and language code, in format: @@ -600,18 +528,20 @@ public function newContentUpdateStruct(): ContentUpdateStruct; public function validate(ValueObject $object, array $context, ?array $fieldIdentifiersToValidate = null): array; /** - * Fetch Content items from the Repository filtered by the given conditions. + * Fetches Content items from the Repository filtered by the given conditions. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @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. */ public function find(Filter $filter, ?array $languages = null): ContentList; /** - * Count total number of items returned by {@see ContentService::find()} method. + * Gets the total number of fetchable Content items. + * + * Counts total number of items returned by {@see ContentService::find()} with the same parameters. * - * @param string[] $languages a list of language codes to be added as additional constraints. + * @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. */ From 02200e652f7e377d0a4d024571fbf53d1ff976e2 Mon Sep 17 00:00:00 2001 From: Bartek Wajda Date: Fri, 8 Nov 2024 16:26:12 +0100 Subject: [PATCH 2/2] IBX-8418: Fixed removing orphaned drafts when trashing or deleting its ancestors (#439) For more details see https://issues.ibexa.co/browse/IBX-8418 and https://github.com/ibexa/core/pull/439 Key changes: * Fixed removing orphaned draft when trashing or deleting its parent or ancestor location --- phpstan-baseline.neon | 25 ++-- .../Persistence/Content/Location/Handler.php | 5 + src/contracts/Test/IbexaKernelTestTrait.php | 6 + src/contracts/Test/IbexaTestKernel.php | 1 + src/lib/Persistence/Cache/LocationHandler.php | 14 +++ .../Persistence/Legacy/Content/Handler.php | 1 + .../Legacy/Content/Location/Gateway.php | 7 ++ .../Location/Gateway/DoctrineDatabase.php | 23 ++++ .../Location/Gateway/ExceptionConversion.php | 12 ++ .../Legacy/Content/Location/Handler.php | 5 + .../Legacy/Content/TreeHandler.php | 20 ++++ src/lib/Repository/TrashService.php | 1 + .../ContentService/DeleteContentTest.php | 111 ++++++++++++++++++ .../Persistence/Cache/LocationHandlerTest.php | 1 + .../Legacy/Content/LocationHandlerTest.php | 12 ++ .../Legacy/Content/TreeHandlerTest.php | 55 +++++++++ 16 files changed, 284 insertions(+), 15 deletions(-) create mode 100644 tests/integration/Core/Repository/ContentService/DeleteContentTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7bfaaed552..668d9e640a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -19010,11 +19010,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/User/Mapper.php - - - message: "#^array\\|string does not accept array\\\\.$#" - count: 1 - path: src/lib/Persistence/Legacy/User/Mapper.php - - message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:addPolicyLimitations\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" count: 1 @@ -27090,23 +27085,18 @@ parameters: count: 5 path: tests/integration/Core/Repository/ContentServiceTest.php - - - message: "#^Cannot access offset 0 on iterable\\\\.$#" - count: 12 - path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 0 on iterable\\\\.$#" count: 7 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 0 on iterable\\\\.$#" - count: 4 + message: "#^Cannot access offset 0 on iterable\\\\.$#" + count: 12 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset 1 on iterable\\\\.$#" + message: "#^Cannot access offset 0 on iterable\\\\.$#" count: 4 path: tests/integration/Core/Repository/ContentServiceTest.php @@ -27115,6 +27105,11 @@ parameters: count: 2 path: tests/integration/Core/Repository/ContentServiceTest.php + - + message: "#^Cannot access offset 1 on iterable\\\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceTest.php + - message: "#^Cannot access offset 1 on iterable\\\\.$#" count: 1 @@ -27141,12 +27136,12 @@ parameters: path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset mixed on iterable\\\\.$#" + message: "#^Cannot access offset int on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php - - message: "#^Cannot access offset int on iterable\\\\.$#" + message: "#^Cannot access offset mixed on iterable\\\\.$#" count: 1 path: tests/integration/Core/Repository/ContentServiceTest.php diff --git a/src/contracts/Persistence/Content/Location/Handler.php b/src/contracts/Persistence/Content/Location/Handler.php index 0fbea26996..5eb76c4c78 100644 --- a/src/contracts/Persistence/Content/Location/Handler.php +++ b/src/contracts/Persistence/Content/Location/Handler.php @@ -215,6 +215,11 @@ public function create(CreateStruct $location); */ public function removeSubtree($locationId); + /** + * Removes all draft contents that have no location assigned to them under the given parent location. + */ + public function deleteChildrenDrafts(int $locationId): void; + /** * Set section on all content objects in the subtree. * Only main locations will be updated. diff --git a/src/contracts/Test/IbexaKernelTestTrait.php b/src/contracts/Test/IbexaKernelTestTrait.php index 6b75669022..c40329634d 100644 --- a/src/contracts/Test/IbexaKernelTestTrait.php +++ b/src/contracts/Test/IbexaKernelTestTrait.php @@ -19,6 +19,7 @@ use Ibexa\Contracts\Core\Repository\RoleService; use Ibexa\Contracts\Core\Repository\SearchService; use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\TrashService; use Ibexa\Contracts\Core\Repository\URLAliasService; use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; @@ -169,6 +170,11 @@ protected static function getUrlAliasService(): URLAliasService return self::getServiceByClassName(URLAliasService::class); } + protected static function getTrashService(): TrashService + { + return self::getServiceByClassName(TrashService::class); + } + protected static function setAnonymousUser(): void { $anonymousUserId = 10; diff --git a/src/contracts/Test/IbexaTestKernel.php b/src/contracts/Test/IbexaTestKernel.php index ca18fc32a7..5c89768f84 100644 --- a/src/contracts/Test/IbexaTestKernel.php +++ b/src/contracts/Test/IbexaTestKernel.php @@ -92,6 +92,7 @@ class IbexaTestKernel extends Kernel implements IbexaTestKernelInterface Repository\TokenService::class, Repository\URLAliasService::class, Repository\BookmarkService::class, + Repository\TrashService::class, Handler::class, ]; diff --git a/src/lib/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php index dc07fd9f93..74a50a2039 100644 --- a/src/lib/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -415,6 +415,20 @@ public function removeSubtree($locationId) return $return; } + public function deleteChildrenDrafts(int $locationId): void + { + $this->logger->logCall(__METHOD__, ['location' => $locationId]); + + $this->persistenceHandler->locationHandler()->deleteChildrenDrafts($locationId); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::LOCATION_PATH_IDENTIFIER, + [$locationId], + ), + ]); + } + /** * {@inheritdoc} */ diff --git a/src/lib/Persistence/Legacy/Content/Handler.php b/src/lib/Persistence/Legacy/Content/Handler.php index 56871b0d27..0acf1ab1e0 100644 --- a/src/lib/Persistence/Legacy/Content/Handler.php +++ b/src/lib/Persistence/Legacy/Content/Handler.php @@ -650,6 +650,7 @@ public function deleteContent($contentId) $this->removeRawContent($contentId); } else { foreach ($contentLocations as $locationId) { + $this->treeHandler->deleteChildrenDrafts($locationId); $this->treeHandler->removeSubtree($locationId); } } diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway.php b/src/lib/Persistence/Legacy/Content/Location/Gateway.php index 57e7838eeb..5218cad951 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway.php @@ -111,6 +111,13 @@ abstract public function loadParentLocationsDataForDraftContent(int $contentId): */ abstract public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array; + /** + * Finds draft contents created under the given parent location. + * + * @return array + */ + abstract public function getSubtreeChildrenDraftContentIds(int $sourceId): array; + abstract public function getSubtreeSize(string $path): int; /** diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index 8341e96196..bfd06ecb2a 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -237,6 +237,29 @@ public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array : $results; } + /** + * @return array + * + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function getSubtreeChildrenDraftContentIds(int $sourceId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('contentobject_id') + ->from('eznode_assignment', 'n') + ->innerJoin('n', 'ezcontentobject', 'c', 'n.contentobject_id = c.id') + ->andWhere('n.parent_node = :parentNode') + ->andWhere('c.status = :status') + ->setParameter(':parentNode', $sourceId, ParameterType::INTEGER) + ->setParameter(':status', ContentInfo::STATUS_DRAFT, ParameterType::INTEGER); + + $statement = $query->execute(); + + return $statement->fetchFirstColumn(); + } + public function getSubtreeSize(string $path): int { $query = $this->createNodeQueryBuilder([$this->dbPlatform->getCountExpression('node_id')]); diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php index 70fe4b717c..e2d3c5db23 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php @@ -106,6 +106,18 @@ public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array } } + /** + * @return array + */ + public function getSubtreeChildrenDraftContentIds(int $sourceId): array + { + try { + return $this->innerGateway->getSubtreeChildrenDraftContentIds($sourceId); + } catch (PDOException $e) { + throw DatabaseException::wrap($e); + } + } + public function getSubtreeSize(string $path): int { try { diff --git a/src/lib/Persistence/Legacy/Content/Location/Handler.php b/src/lib/Persistence/Legacy/Content/Location/Handler.php index 758e221c4a..8ce6a52493 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Handler.php +++ b/src/lib/Persistence/Legacy/Content/Location/Handler.php @@ -546,6 +546,11 @@ public function removeSubtree($locationId) $this->treeHandler->removeSubtree($locationId); } + public function deleteChildrenDrafts(int $locationId): void + { + $this->treeHandler->deleteChildrenDrafts($locationId); + } + /** * Set section on all content objects in the subtree. * diff --git a/src/lib/Persistence/Legacy/Content/TreeHandler.php b/src/lib/Persistence/Legacy/Content/TreeHandler.php index 700b067e94..629326589b 100644 --- a/src/lib/Persistence/Legacy/Content/TreeHandler.php +++ b/src/lib/Persistence/Legacy/Content/TreeHandler.php @@ -215,6 +215,26 @@ public function removeSubtree($locationId) $this->locationGateway->deleteNodeAssignment($contentId); } + /** + * Removes draft contents assigned to the given parent location and its descendant locations. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function deleteChildrenDrafts(int $locationId): void + { + $subLocations = $this->locationGateway->getChildren($locationId); + foreach ($subLocations as $subLocation) { + $this->deleteChildrenDrafts($subLocation['node_id']); + } + + // Fetch child draft content ids + $subtreeChildrenDraftIds = $this->locationGateway->getSubtreeChildrenDraftContentIds($locationId); + + foreach ($subtreeChildrenDraftIds as $contentId) { + $this->removeRawContent($contentId); + } + } + /** * Set section on all content objects in the subtree. * diff --git a/src/lib/Repository/TrashService.php b/src/lib/Repository/TrashService.php index e32a0cea27..3ab569d18a 100644 --- a/src/lib/Repository/TrashService.php +++ b/src/lib/Repository/TrashService.php @@ -146,6 +146,7 @@ public function trash(Location $location): ?APITrashItem $this->repository->beginTransaction(); try { + $this->persistenceHandler->locationHandler()->deleteChildrenDrafts($location->id); $spiTrashItem = $this->persistenceHandler->trashHandler()->trashSubtree($location->id); $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); $this->repository->commit(); diff --git a/tests/integration/Core/Repository/ContentService/DeleteContentTest.php b/tests/integration/Core/Repository/ContentService/DeleteContentTest.php new file mode 100644 index 0000000000..e1dd1faf7a --- /dev/null +++ b/tests/integration/Core/Repository/ContentService/DeleteContentTest.php @@ -0,0 +1,111 @@ +prepareContentStructure(); + + $contentService->deleteContent($folder->getContentInfo()); + + $contentInfos = $contentService->loadContentInfoList([ + $draft1->getId(), + $draft2->getId(), + $draft3->getId(), + $draftSecondDepth->getId(), + ]); + + self::assertEmpty($contentInfos); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testTrashLocationDeletesChildrenDrafts(): void + { + $trashService = self::getTrashService(); + $contentService = self::getContentService(); + + [$folder, $draft1, $draft2, $draft3, $draftSecondDepth] = $this->prepareContentStructure(); + + $folderMainLocationId = $folder->getVersionInfo()->getContentInfo()->getMainLocationId(); + Assert::assertIsNumeric($folderMainLocationId); + + $locationToTrash = self::getLocationService()->loadLocation($folderMainLocationId); + + $trashService->trash($locationToTrash); + + $contentInfos = $contentService->loadContentInfoList([ + $draft1->getId(), + $draft2->getId(), + $draft3->getId(), + $draftSecondDepth->getId(), + ]); + + self::assertEmpty($contentInfos); + } + + /** + * @return array + */ + private function prepareContentStructure(): array + { + $folder = $this->createFolder(['eng-GB' => 'Folder'], 2); + $folderMainLocationId = $folder->getVersionInfo()->getContentInfo()->getMainLocationId(); + Assert::assertIsNumeric($folderMainLocationId); + + $childFolder = $this->createFolder( + ['eng-GB' => 'Child folder'], + $folderMainLocationId, + ); + $childFolderMainLocationId = $childFolder->getVersionInfo()->getContentInfo()->getMainLocationId(); + Assert::assertIsNumeric($childFolderMainLocationId); + + $secondDepthChildFolder = $this->createFolder( + ['eng-GB' => 'Second depth folder'], + $childFolderMainLocationId, + ); + $secondDepthChildFolderLocationId = $secondDepthChildFolder + ->getVersionInfo() + ->getContentInfo() + ->getMainLocationId() + ; + Assert::assertIsNumeric($secondDepthChildFolderLocationId); + + $draft1 = $this->createFolderDraft(['eng-GB' => 'Folder draft 1'], $folderMainLocationId); + $draft2 = $this->createFolderDraft(['eng-GB' => 'Folder draft 2'], $childFolderMainLocationId); + $draft3 = $this->createFolderDraft(['eng-GB' => 'Folder draft 3'], $childFolderMainLocationId); + $draftSecondDepth = $this->createFolderDraft( + ['eng-GB' => 'Folder draft 4'], + $secondDepthChildFolderLocationId, + ); + + return [ + $folder, + $draft1, + $draft2, + $draft3, + $draftSecondDepth, + ]; + } +} diff --git a/tests/lib/Persistence/Cache/LocationHandlerTest.php b/tests/lib/Persistence/Cache/LocationHandlerTest.php index ed498aa495..c06f71b276 100644 --- a/tests/lib/Persistence/Cache/LocationHandlerTest.php +++ b/tests/lib/Persistence/Cache/LocationHandlerTest.php @@ -67,6 +67,7 @@ public function providerForUnCachedMethods(): array ['c-4', 'ragl-4'], ], ['removeSubtree', [12], [['location_path', [12], false]], null, ['lp-12']], + ['deleteChildrenDrafts', [12], [['location_path', [12], false]], null, ['lp-12']], ['setSectionForSubtree', [12, 2], [['location_path', [12], false]], null, ['lp-12']], ['changeMainLocation', [4, 12], [['content', [4], false]], null, ['c-4']], ['countLocationsByContent', [4]], diff --git a/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php b/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php index 6b971543cc..b22dd45d60 100644 --- a/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php @@ -453,6 +453,18 @@ public function testRemoveSubtree() $handler->removeSubtree(42); } + public function testDeleteChildrenDrafts(): void + { + $handler = $this->getLocationHandler(); + + $this->treeHandler + ->expects(self::once()) + ->method('deleteChildrenDrafts') + ->with(42); + + $handler->deleteChildrenDrafts(42); + } + /** * Test for the copySubtree() method. */ diff --git a/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php b/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php index a3403d8fb8..9daf814d36 100644 --- a/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php @@ -400,6 +400,61 @@ public function testLoadLocation() $this->assertTrue($location instanceof Location); } + public function testDeleteChildrenDraftsRecursive(): void + { + $locationGatewayMock = $this->getLocationGatewayMock(); + $contentGatewayMock = $this->getContentGatewayMock(); + $contentMapperMock = $this->getContentMapperMock(); + + $locationGatewayMock + ->expects(self::exactly(3)) + ->method('getChildren') + ->willReturnMap([ + [42, [ + ['node_id' => 201], + ['node_id' => 202], + ]], + [201, []], + [202, []], + ]); + + $locationGatewayMock + ->expects(self::exactly(3)) + ->method('getSubtreeChildrenDraftContentIds') + ->willReturnMap([ + [201, [101]], + [202, [102]], + [42, [99]], + ]); + + $contentGatewayMock + ->expects(self::exactly(3)) + ->method('loadContentInfo') + ->willReturnMap([ + [101, ['main_node_id' => 201]], + [102, ['main_node_id' => 202]], + [99, ['main_node_id' => 42]], + ]); + + $contentMapperMock + ->expects(self::exactly(3)) + ->method('extractContentInfoFromRow') + ->willReturnCallback(static function (array $row): ContentInfo { + return new ContentInfo(['mainLocationId' => $row['main_node_id']]); + }); + + $contentGatewayMock + ->expects(self::exactly(3)) + ->method('deleteContent') + ->willReturnCallback(static function (int $contentId): void { + self::assertContains($contentId, [99, 101, 102]); + }); + + $treeHandler = $this->getTreeHandler(); + + $treeHandler->deleteChildrenDrafts(42); + } + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Location\Gateway */ protected $locationGatewayMock;