From 04544ac0d1c322c58a637ef580957f1495161920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Thu, 16 Jul 2026 08:41:18 +0200 Subject: [PATCH 1/8] IBX-12029: Added content publication strategy contract with synchronous default strategy --- .../IbexaCoreExtension.php | 1 + .../ContentPublicationStrategyInterface.php | 36 +++++++++++ .../ChainContentPublicationStrategy.php | 59 +++++++++++++++++++ .../SynchronousContentPublicationStrategy.php | 38 ++++++++++++ .../settings/content_publication.yml | 22 +++++++ 5 files changed, 156 insertions(+) create mode 100644 src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php create mode 100644 src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php create mode 100644 src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php create mode 100644 src/lib/Resources/settings/content_publication.yml diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php index 184db428f4..cc317b20a2 100644 --- a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -370,6 +370,7 @@ private function handleApiLoading(ContainerBuilder $container, FileLoader $loade $coreLoader->load('user_preference.yml'); $coreLoader->load('events.yml'); $coreLoader->load('thumbnails.yml'); + $coreLoader->load('content_publication.yml'); $coreLoader->load('tokens.yml'); $coreLoader->load('content_location_mapper.yml'); diff --git a/src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php b/src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php new file mode 100644 index 0000000000..61f0531f1c --- /dev/null +++ b/src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php @@ -0,0 +1,36 @@ + $translations List of language codes of translations which will be + * included in a published version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): void; +} diff --git a/src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php b/src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php new file mode 100644 index 0000000000..77661046f8 --- /dev/null +++ b/src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php @@ -0,0 +1,59 @@ + $strategies + */ + public function __construct( + private iterable $strategies, + ) { + } + + public function supports(): bool + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports()) { + return true; + } + } + + return false; + } + + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): void + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports()) { + $strategy->publishVersion($versionInfo, $translations); + + return; + } + } + + throw new LogicException(sprintf( + 'No content publication strategy supports the current publication. At least %s' + . ' must be tagged with "ibexa.repository.content.publication_strategy".', + SynchronousContentPublicationStrategy::class, + )); + } +} diff --git a/src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php b/src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php new file mode 100644 index 0000000000..7bbf68c4d8 --- /dev/null +++ b/src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php @@ -0,0 +1,38 @@ +contentService->publishVersion($versionInfo, $translations); + } +} diff --git a/src/lib/Resources/settings/content_publication.yml b/src/lib/Resources/settings/content_publication.yml new file mode 100644 index 0000000000..b3189635de --- /dev/null +++ b/src/lib/Resources/settings/content_publication.yml @@ -0,0 +1,22 @@ +services: + _defaults: + public: false + autoconfigure: true + autowire: true + + # Content publication strategies. The first strategy (in priority order) whose supports() + # returns true executes; the synchronous strategy is the always-applicable fallback. + # Additional strategies (e.g. asynchronous publication) are provided by dedicated packages + # via the "ibexa.repository.content.publication_strategy" tag with a higher priority. + Ibexa\Core\Repository\Strategy\Publication\SynchronousContentPublicationStrategy: + arguments: + $contentService: '@ibexa.api.service.content' + tags: + - { name: ibexa.repository.content.publication_strategy, priority: -100 } + + Ibexa\Core\Repository\Strategy\Publication\ChainContentPublicationStrategy: + arguments: + $strategies: !tagged_iterator ibexa.repository.content.publication_strategy + + Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface: + alias: Ibexa\Core\Repository\Strategy\Publication\ChainContentPublicationStrategy From 06fe63be5a08b7de407cc926eca176d7e3951176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Thu, 16 Jul 2026 08:41:18 +0200 Subject: [PATCH 2/8] IBX-12029: Added unit tests for content publication strategies --- .../ChainContentPublicationStrategyTest.php | 88 +++++++++++++++++++ ...chronousContentPublicationStrategyTest.php | 55 ++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php create mode 100644 tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php diff --git a/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php new file mode 100644 index 0000000000..a09dcde96e --- /dev/null +++ b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php @@ -0,0 +1,88 @@ +createMock(VersionInfo::class); + + $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $notSupportingStrategy->method('supports')->willReturn(false); + $notSupportingStrategy + ->expects(self::never()) + ->method('publishVersion'); + + $supportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $supportingStrategy->method('supports')->willReturn(true); + $supportingStrategy + ->expects(self::once()) + ->method('publishVersion') + ->with(self::identicalTo($versionInfo), ['eng-GB']); + + $neverConsultedStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $neverConsultedStrategy + ->expects(self::never()) + ->method('supports'); + $neverConsultedStrategy + ->expects(self::never()) + ->method('publishVersion'); + + $chain = new ChainContentPublicationStrategy([ + $notSupportingStrategy, + $supportingStrategy, + $neverConsultedStrategy, + ]); + + $chain->publishVersion($versionInfo, ['eng-GB']); + } + + public function testSupportsReturnsTrueWhenAnyStrategySupports(): void + { + $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $notSupportingStrategy->method('supports')->willReturn(false); + + $supportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $supportingStrategy->method('supports')->willReturn(true); + + $chain = new ChainContentPublicationStrategy([$notSupportingStrategy, $supportingStrategy]); + + self::assertTrue($chain->supports()); + } + + public function testSupportsReturnsFalseForEmptyChain(): void + { + $chain = new ChainContentPublicationStrategy([]); + + self::assertFalse($chain->supports()); + } + + public function testPublishVersionThrowsLogicExceptionWhenNoStrategySupports(): void + { + $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); + $notSupportingStrategy->method('supports')->willReturn(false); + $notSupportingStrategy + ->expects(self::never()) + ->method('publishVersion'); + + $chain = new ChainContentPublicationStrategy([$notSupportingStrategy]); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('No content publication strategy supports the current publication.'); + + $chain->publishVersion($this->createMock(VersionInfo::class)); + } +} diff --git a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php new file mode 100644 index 0000000000..e9242944ab --- /dev/null +++ b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php @@ -0,0 +1,55 @@ +createMock(ContentService::class) + ); + + self::assertTrue($strategy->supports()); + } + + public function testPublishVersionDelegatesToContentService(): void + { + $versionInfo = $this->createMock(VersionInfo::class); + + $contentService = $this->createMock(ContentService::class); + $contentService + ->expects(self::once()) + ->method('publishVersion') + ->with(self::identicalTo($versionInfo), ['ger-DE']); + + $strategy = new SynchronousContentPublicationStrategy($contentService); + $strategy->publishVersion($versionInfo, ['ger-DE']); + } + + public function testPublishVersionDefaultsToAllTranslations(): void + { + $versionInfo = $this->createMock(VersionInfo::class); + + $contentService = $this->createMock(ContentService::class); + $contentService + ->expects(self::once()) + ->method('publishVersion') + ->with(self::identicalTo($versionInfo), Language::ALL); + + $strategy = new SynchronousContentPublicationStrategy($contentService); + $strategy->publishVersion($versionInfo); + } +} From 80a9a6ef0566fca30ef47e089e7275d06f300351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Thu, 16 Jul 2026 10:59:54 +0200 Subject: [PATCH 3/8] IBX-12029: Added improvements --- .../ContentPublicationStrategyInterface.php | 4 +-- .../ChainContentPublicationStrategy.php | 6 ++-- .../SynchronousContentPublicationStrategy.php | 6 ++-- .../settings/content_publication.yml | 8 ++--- .../ChainContentPublicationStrategyTest.php | 31 ++++++++++++++----- ...chronousContentPublicationStrategyTest.php | 6 ++-- 6 files changed, 38 insertions(+), 23 deletions(-) rename src/contracts/Repository/Strategy/{Publication => ContentPublication}/ContentPublicationStrategyInterface.php (89%) rename src/lib/Repository/Strategy/{Publication => ContentPublication}/ChainContentPublicationStrategy.php (85%) rename src/lib/Repository/Strategy/{Publication => ContentPublication}/SynchronousContentPublicationStrategy.php (83%) diff --git a/src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php similarity index 89% rename from src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php rename to src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php index 61f0531f1c..4a4ef37d5b 100644 --- a/src/contracts/Repository/Strategy/Publication/ContentPublicationStrategyInterface.php +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\Contracts\Core\Repository\Strategy\Publication; +namespace Ibexa\Contracts\Core\Repository\Strategy\ContentPublication; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; @@ -21,7 +21,7 @@ interface ContentPublicationStrategyInterface { /** - * Whether this strategy handles content publication for the current repository. + * Whether this strategy handles content publication. */ public function supports(): bool; diff --git a/src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php b/src/lib/Repository/Strategy/ContentPublication/ChainContentPublicationStrategy.php similarity index 85% rename from src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php rename to src/lib/Repository/Strategy/ContentPublication/ChainContentPublicationStrategy.php index 77661046f8..e2288aabbd 100644 --- a/src/lib/Repository/Strategy/Publication/ChainContentPublicationStrategy.php +++ b/src/lib/Repository/Strategy/ContentPublication/ChainContentPublicationStrategy.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace Ibexa\Core\Repository\Strategy\Publication; +namespace Ibexa\Core\Repository\Strategy\ContentPublication; -use Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface; +use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use LogicException; @@ -22,7 +22,7 @@ final readonly class ChainContentPublicationStrategy implements ContentPublicationStrategyInterface { /** - * @param iterable<\Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface> $strategies + * @param iterable $strategies */ public function __construct( private iterable $strategies, diff --git a/src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php b/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php similarity index 83% rename from src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php rename to src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php index 7bbf68c4d8..870142b804 100644 --- a/src/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategy.php +++ b/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace Ibexa\Core\Repository\Strategy\Publication; +namespace Ibexa\Core\Repository\Strategy\ContentPublication; use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface; +use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; @@ -17,7 +17,7 @@ * Synchronous publication strategy: publishes the version inside the current request. * Always-applicable fallback, registered with the lowest priority. * - * @internal Meant for internal use by Repository + * @internal */ final readonly class SynchronousContentPublicationStrategy implements ContentPublicationStrategyInterface { diff --git a/src/lib/Resources/settings/content_publication.yml b/src/lib/Resources/settings/content_publication.yml index b3189635de..87a0162440 100644 --- a/src/lib/Resources/settings/content_publication.yml +++ b/src/lib/Resources/settings/content_publication.yml @@ -8,15 +8,15 @@ services: # returns true executes; the synchronous strategy is the always-applicable fallback. # Additional strategies (e.g. asynchronous publication) are provided by dedicated packages # via the "ibexa.repository.content.publication_strategy" tag with a higher priority. - Ibexa\Core\Repository\Strategy\Publication\SynchronousContentPublicationStrategy: + Ibexa\Core\Repository\Strategy\ContentPublication\SynchronousContentPublicationStrategy: arguments: $contentService: '@ibexa.api.service.content' tags: - { name: ibexa.repository.content.publication_strategy, priority: -100 } - Ibexa\Core\Repository\Strategy\Publication\ChainContentPublicationStrategy: + Ibexa\Core\Repository\Strategy\ContentPublication\ChainContentPublicationStrategy: arguments: $strategies: !tagged_iterator ibexa.repository.content.publication_strategy - Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface: - alias: Ibexa\Core\Repository\Strategy\Publication\ChainContentPublicationStrategy + Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface: + alias: Ibexa\Core\Repository\Strategy\ContentPublication\ChainContentPublicationStrategy diff --git a/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php index a09dcde96e..acf65023a9 100644 --- a/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php +++ b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php @@ -8,9 +8,9 @@ namespace Ibexa\Tests\Core\Repository\Strategy\Publication; -use Ibexa\Contracts\Core\Repository\Strategy\Publication\ContentPublicationStrategyInterface; +use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; -use Ibexa\Core\Repository\Strategy\Publication\ChainContentPublicationStrategy; +use Ibexa\Core\Repository\Strategy\ContentPublication\ChainContentPublicationStrategy; use LogicException; use PHPUnit\Framework\TestCase; @@ -18,16 +18,22 @@ final class ChainContentPublicationStrategyTest extends TestCase { public function testPublishVersionExecutesFirstSupportingStrategy(): void { - $versionInfo = $this->createMock(VersionInfo::class); + $versionInfo = $this->createStub(VersionInfo::class); $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); - $notSupportingStrategy->method('supports')->willReturn(false); + $notSupportingStrategy + ->expects(self::once()) + ->method('supports') + ->willReturn(false); $notSupportingStrategy ->expects(self::never()) ->method('publishVersion'); $supportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); - $supportingStrategy->method('supports')->willReturn(true); + $supportingStrategy + ->expects(self::once()) + ->method('supports') + ->willReturn(true); $supportingStrategy ->expects(self::once()) ->method('publishVersion') @@ -53,10 +59,16 @@ public function testPublishVersionExecutesFirstSupportingStrategy(): void public function testSupportsReturnsTrueWhenAnyStrategySupports(): void { $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); - $notSupportingStrategy->method('supports')->willReturn(false); + $notSupportingStrategy + ->expects(self::once()) + ->method('supports') + ->willReturn(false); $supportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); - $supportingStrategy->method('supports')->willReturn(true); + $supportingStrategy + ->expects(self::once()) + ->method('supports') + ->willReturn(true); $chain = new ChainContentPublicationStrategy([$notSupportingStrategy, $supportingStrategy]); @@ -73,7 +85,10 @@ public function testSupportsReturnsFalseForEmptyChain(): void public function testPublishVersionThrowsLogicExceptionWhenNoStrategySupports(): void { $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); - $notSupportingStrategy->method('supports')->willReturn(false); + $notSupportingStrategy + ->expects(self::once()) + ->method('supports') + ->willReturn(false); $notSupportingStrategy ->expects(self::never()) ->method('publishVersion'); diff --git a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php index e9242944ab..e60d2cafd7 100644 --- a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php +++ b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php @@ -11,7 +11,7 @@ use Ibexa\Contracts\Core\Repository\ContentService; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; -use Ibexa\Core\Repository\Strategy\Publication\SynchronousContentPublicationStrategy; +use Ibexa\Core\Repository\Strategy\ContentPublication\SynchronousContentPublicationStrategy; use PHPUnit\Framework\TestCase; final class SynchronousContentPublicationStrategyTest extends TestCase @@ -19,7 +19,7 @@ final class SynchronousContentPublicationStrategyTest extends TestCase public function testSupportsAlwaysReturnsTrue(): void { $strategy = new SynchronousContentPublicationStrategy( - $this->createMock(ContentService::class) + $this->createStub(ContentService::class) ); self::assertTrue($strategy->supports()); @@ -27,7 +27,7 @@ public function testSupportsAlwaysReturnsTrue(): void public function testPublishVersionDelegatesToContentService(): void { - $versionInfo = $this->createMock(VersionInfo::class); + $versionInfo = $this->createStub(VersionInfo::class); $contentService = $this->createMock(ContentService::class); $contentService From 174cfa96cf51b779b45f85795f4196ac2050cda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Thu, 16 Jul 2026 11:03:51 +0200 Subject: [PATCH 4/8] IBX-12029: Improved phpstan languages type --- .../ContentPublication/ContentPublicationStrategyInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php index 4a4ef37d5b..a94f36b3d2 100644 --- a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php @@ -26,7 +26,7 @@ interface ContentPublicationStrategyInterface public function supports(): bool; /** - * @param array $translations List of language codes of translations which will be + * @param list $translations List of language codes of translations which will be * included in a published version * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException From fa6aa37991aa6c33074336799f4f88e69c8d8496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Thu, 16 Jul 2026 14:00:02 +0200 Subject: [PATCH 5/8] IBX-12029: Addressed CR remarks --- .../IbexaCoreExtension.php | 2 +- .../ContentPublicationStrategyInterface.php | 2 +- ...blication.yml => content_publication.yaml} | 0 ...chronousContentPublicationStrategyTest.php | 40 +++++++++++-------- 4 files changed, 26 insertions(+), 18 deletions(-) rename src/lib/Resources/settings/{content_publication.yml => content_publication.yaml} (100%) diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php index cc317b20a2..5473aeff52 100644 --- a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -370,7 +370,7 @@ private function handleApiLoading(ContainerBuilder $container, FileLoader $loade $coreLoader->load('user_preference.yml'); $coreLoader->load('events.yml'); $coreLoader->load('thumbnails.yml'); - $coreLoader->load('content_publication.yml'); + $coreLoader->load('content_publication.yaml'); $coreLoader->load('tokens.yml'); $coreLoader->load('content_location_mapper.yml'); diff --git a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php index a94f36b3d2..faebfab287 100644 --- a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php @@ -27,7 +27,7 @@ public function supports(): bool; /** * @param list $translations List of language codes of translations which will be - * included in a published version + * included in a published version * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException diff --git a/src/lib/Resources/settings/content_publication.yml b/src/lib/Resources/settings/content_publication.yaml similarity index 100% rename from src/lib/Resources/settings/content_publication.yml rename to src/lib/Resources/settings/content_publication.yaml diff --git a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php index e60d2cafd7..94af937b2b 100644 --- a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php +++ b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php @@ -25,31 +25,39 @@ public function testSupportsAlwaysReturnsTrue(): void self::assertTrue($strategy->supports()); } - public function testPublishVersionDelegatesToContentService(): void - { + /** + * @dataProvider providerForTestPublishVersionDelegatesToContentService + * + * @param list|null $translations + * @param list $expectedTranslations + */ + public function testPublishVersionDelegatesToContentService( + ?array $translations, + array $expectedTranslations + ): void { $versionInfo = $this->createStub(VersionInfo::class); $contentService = $this->createMock(ContentService::class); $contentService ->expects(self::once()) ->method('publishVersion') - ->with(self::identicalTo($versionInfo), ['ger-DE']); + ->with(self::identicalTo($versionInfo), $expectedTranslations); + + $publishArguments = ['versionInfo' => $versionInfo]; + if (null !== $translations) { + $publishArguments['translations'] = $translations; + } - $strategy = new SynchronousContentPublicationStrategy($contentService); - $strategy->publishVersion($versionInfo, ['ger-DE']); + (new SynchronousContentPublicationStrategy($contentService)) + ->publishVersion(...$publishArguments); } - public function testPublishVersionDefaultsToAllTranslations(): void + /** + * @return iterable|null, list}> + */ + public static function providerForTestPublishVersionDelegatesToContentService(): iterable { - $versionInfo = $this->createMock(VersionInfo::class); - - $contentService = $this->createMock(ContentService::class); - $contentService - ->expects(self::once()) - ->method('publishVersion') - ->with(self::identicalTo($versionInfo), Language::ALL); - - $strategy = new SynchronousContentPublicationStrategy($contentService); - $strategy->publishVersion($versionInfo); + yield 'explicit translations' => [['ger-DE'], ['ger-DE']]; + yield 'defaults to all translations' => [null, Language::ALL]; } } From 295e4362eb9fad0d528bbd6d4c01317216b1144d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Fri, 17 Jul 2026 07:18:08 +0200 Subject: [PATCH 6/8] IBX-12029: Removed not needed comment --- .../ContentPublication/ContentPublicationStrategyInterface.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php index faebfab287..a27c86f6a2 100644 --- a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationStrategyInterface.php @@ -20,9 +20,6 @@ */ interface ContentPublicationStrategyInterface { - /** - * Whether this strategy handles content publication. - */ public function supports(): bool; /** From bd7df8416bd6e8bb2ec1e5abfbc180a9c7bb4795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Fri, 17 Jul 2026 11:48:16 +0200 Subject: [PATCH 7/8] IBX-12029: Introduced ContentPublicationResult --- .../ContentPublicationResult.php | 19 +++++++++++++++++++ .../ContentPublicationStrategyInterface.php | 2 +- .../ChainContentPublicationStrategy.php | 7 +++---- .../SynchronousContentPublicationStrategy.php | 7 +++++-- .../ChainContentPublicationStrategyTest.php | 7 +++++-- ...chronousContentPublicationStrategyTest.php | 9 +++++++-- 6 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php diff --git a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php new file mode 100644 index 0000000000..f56eaa581a --- /dev/null +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php @@ -0,0 +1,19 @@ +strategies as $strategy) { if ($strategy->supports()) { - $strategy->publishVersion($versionInfo, $translations); - - return; + return $strategy->publishVersion($versionInfo, $translations); } } diff --git a/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php b/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php index 870142b804..606812575f 100644 --- a/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php +++ b/src/lib/Repository/Strategy/ContentPublication/SynchronousContentPublicationStrategy.php @@ -9,6 +9,7 @@ namespace Ibexa\Core\Repository\Strategy\ContentPublication; use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationResult; use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; @@ -31,8 +32,10 @@ public function supports(): bool return true; } - public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): void + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): ContentPublicationResult { - $this->contentService->publishVersion($versionInfo, $translations); + return new ContentPublicationResult( + $this->contentService->publishVersion($versionInfo, $translations) + ); } } diff --git a/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php index acf65023a9..4fdbe1e1d3 100644 --- a/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php +++ b/tests/lib/Repository/Strategy/Publication/ChainContentPublicationStrategyTest.php @@ -8,6 +8,7 @@ namespace Ibexa\Tests\Core\Repository\Strategy\Publication; +use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationResult; use Ibexa\Contracts\Core\Repository\Strategy\ContentPublication\ContentPublicationStrategyInterface; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use Ibexa\Core\Repository\Strategy\ContentPublication\ChainContentPublicationStrategy; @@ -19,6 +20,7 @@ final class ChainContentPublicationStrategyTest extends TestCase public function testPublishVersionExecutesFirstSupportingStrategy(): void { $versionInfo = $this->createStub(VersionInfo::class); + $result = new ContentPublicationResult(null); $notSupportingStrategy = $this->createMock(ContentPublicationStrategyInterface::class); $notSupportingStrategy @@ -37,7 +39,8 @@ public function testPublishVersionExecutesFirstSupportingStrategy(): void $supportingStrategy ->expects(self::once()) ->method('publishVersion') - ->with(self::identicalTo($versionInfo), ['eng-GB']); + ->with(self::identicalTo($versionInfo), ['eng-GB']) + ->willReturn($result); $neverConsultedStrategy = $this->createMock(ContentPublicationStrategyInterface::class); $neverConsultedStrategy @@ -53,7 +56,7 @@ public function testPublishVersionExecutesFirstSupportingStrategy(): void $neverConsultedStrategy, ]); - $chain->publishVersion($versionInfo, ['eng-GB']); + self::assertSame($result, $chain->publishVersion($versionInfo, ['eng-GB'])); } public function testSupportsReturnsTrueWhenAnyStrategySupports(): void diff --git a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php index 94af937b2b..27bc27b868 100644 --- a/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php +++ b/tests/lib/Repository/Strategy/Publication/SynchronousContentPublicationStrategyTest.php @@ -9,6 +9,7 @@ namespace Ibexa\Tests\Core\Repository\Strategy\Publication; use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use Ibexa\Core\Repository\Strategy\ContentPublication\SynchronousContentPublicationStrategy; @@ -36,20 +37,24 @@ public function testPublishVersionDelegatesToContentService( array $expectedTranslations ): void { $versionInfo = $this->createStub(VersionInfo::class); + $content = $this->createStub(Content::class); $contentService = $this->createMock(ContentService::class); $contentService ->expects(self::once()) ->method('publishVersion') - ->with(self::identicalTo($versionInfo), $expectedTranslations); + ->with(self::identicalTo($versionInfo), $expectedTranslations) + ->willReturn($content); $publishArguments = ['versionInfo' => $versionInfo]; if (null !== $translations) { $publishArguments['translations'] = $translations; } - (new SynchronousContentPublicationStrategy($contentService)) + $result = (new SynchronousContentPublicationStrategy($contentService)) ->publishVersion(...$publishArguments); + + self::assertSame($content, $result->publishedContent); } /** From e44db5ad05b800e74ea4d30182ff0d042886c800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Nowak?= Date: Fri, 17 Jul 2026 11:58:01 +0200 Subject: [PATCH 8/8] IBX-12029: Added @experimental phpdoc to ContentPublicationResult --- .../Strategy/ContentPublication/ContentPublicationResult.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php index f56eaa581a..7db844133f 100644 --- a/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php +++ b/src/contracts/Repository/Strategy/ContentPublication/ContentPublicationResult.php @@ -10,6 +10,9 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Content; +/** + * @experimental This contract may change in future. + */ final readonly class ContentPublicationResult { public function __construct(