diff --git a/.github/workflows/test-application.yaml b/.github/workflows/test-application.yaml index a710a86f..bc811226 100644 --- a/.github/workflows/test-application.yaml +++ b/.github/workflows/test-application.yaml @@ -41,7 +41,7 @@ jobs: dependency-versions: 'highest' php-extensions: 'ctype, iconv, mysql, imagick' tools: 'composer:v2' - phpstan: true + phpstan: false lint: false env: SYMFONY_DEPRECATIONS_HELPER: weak @@ -66,7 +66,7 @@ jobs: dependency-versions: 'highest' php-extensions: 'ctype, iconv, mysql, imagick' tools: 'composer:v2' - phpstan: false + phpstan: true lint: true env: SYMFONY_DEPRECATIONS_HELPER: weak diff --git a/Application/Message/ApplyWorkflowTransitionArticleMessage.php b/Application/Message/ApplyWorkflowTransitionArticleMessage.php index 4f4803eb..8e316367 100644 --- a/Application/Message/ApplyWorkflowTransitionArticleMessage.php +++ b/Application/Message/ApplyWorkflowTransitionArticleMessage.php @@ -14,9 +14,9 @@ class ApplyWorkflowTransitionArticleMessage { /** - * @param array{ - * uuid?: string - * } $identifier + * @var array{ + * uuid?: string, + * } */ private $identifier; @@ -43,9 +43,9 @@ public function __construct(array $identifier, string $locale, string $transitio } /** - * @param array{ + * @return array{ * uuid?: string - * } $identifier + * } */ public function getIdentifier(): array { diff --git a/Application/Message/CopyLocaleArticleMessage.php b/Application/Message/CopyLocaleArticleMessage.php index 52b8b3dc..b1dcde01 100644 --- a/Application/Message/CopyLocaleArticleMessage.php +++ b/Application/Message/CopyLocaleArticleMessage.php @@ -5,9 +5,9 @@ class CopyLocaleArticleMessage { /** - * @param array{ + * @var array{ * uuid?: string - * } $identifier + * } */ private $identifier; @@ -34,9 +34,9 @@ public function __construct($identifier, string $sourceLocale, string $targetLoc } /** - * @param array{ + * @return array{ * uuid?: string - * } $identifier + * } */ public function getIdentifier() { diff --git a/Application/Message/CreateArticleMessage.php b/Application/Message/CreateArticleMessage.php index f1b289ef..4b90bc34 100644 --- a/Application/Message/CreateArticleMessage.php +++ b/Application/Message/CreateArticleMessage.php @@ -23,15 +23,28 @@ class CreateArticleMessage */ private $data; + /** + * @var string|null + */ + private $uuid; + /** * @param mixed[] $data */ public function __construct(array $data) { + $uuid = $data['uuid'] ?? null; + Assert::string($data['locale'] ?? null, 'Expected a "locale" string given.'); - Assert::nullOrString($data['uuid'] ?? null, 'Expected "uuid" to be a string.'); + Assert::nullOrString($uuid, 'Expected "uuid" to be a string.'); $this->data = $data; + $this->uuid = $uuid; + } + + public function getUuid(): ?string + { + return $this->uuid; } /** diff --git a/Application/Message/ModifyArticleMessage.php b/Application/Message/ModifyArticleMessage.php index 0f5a2eff..3fbf3211 100644 --- a/Application/Message/ModifyArticleMessage.php +++ b/Application/Message/ModifyArticleMessage.php @@ -19,9 +19,9 @@ class ModifyArticleMessage { /** - * @param array{ + * @var array{ * uuid?: string - * } $identifier + * } */ private $identifier; @@ -45,9 +45,9 @@ public function __construct(array $identifier, array $data) } /** - * @param array{ + * @return array{ * uuid?: string - * } $identifier + * } */ public function getIdentifier(): array { @@ -55,7 +55,7 @@ public function getIdentifier(): array } /** - * @var mixed[] + * @return mixed[] */ public function getData(): array { diff --git a/Application/Message/RemoveArticleMessage.php b/Application/Message/RemoveArticleMessage.php index e3bf810a..0fdf49e7 100644 --- a/Application/Message/RemoveArticleMessage.php +++ b/Application/Message/RemoveArticleMessage.php @@ -17,9 +17,9 @@ class RemoveArticleMessage { /** - * @param array{ + * @var array{ * uuid?: string - * } $identifier + * } */ private $identifier; @@ -34,9 +34,9 @@ public function __construct(array $identifier) } /** - * @param array{ + * @return array{ * uuid?: string - * } $identifier + * } */ public function getIdentifier(): array { diff --git a/Application/MessageHandler/CopyLocaleArticleMessageHandler.php b/Application/MessageHandler/CopyLocaleArticleMessageHandler.php index 2500988e..27349d1c 100644 --- a/Application/MessageHandler/CopyLocaleArticleMessageHandler.php +++ b/Application/MessageHandler/CopyLocaleArticleMessageHandler.php @@ -44,7 +44,7 @@ public function __construct( public function __invoke(CopyLocaleArticleMessage $message): void { - $article = $this->articleRepository->findOneBy($message->getIdentifier()); + $article = $this->articleRepository->getOneBy($message->getIdentifier()); $this->contentCopier->copy( $article, diff --git a/Application/MessageHandler/CreateArticleMessageHandler.php b/Application/MessageHandler/CreateArticleMessageHandler.php index 6a9002c3..e38b9b0f 100644 --- a/Application/MessageHandler/CreateArticleMessageHandler.php +++ b/Application/MessageHandler/CreateArticleMessageHandler.php @@ -34,6 +34,9 @@ final class CreateArticleMessageHandler */ private $articleMappers; + /** + * @param iterable $articleMappers + */ public function __construct( ArticleRepositoryInterface $articleRepository, iterable $articleMappers @@ -45,7 +48,7 @@ public function __construct( public function __invoke(CreateArticleMessage $message): ArticleInterface { $data = $message->getData(); - $article = $this->articleRepository->createNew($data['uuid'] ?? null); + $article = $this->articleRepository->createNew($message->getUuid()); foreach ($this->articleMappers as $articleMapper) { $articleMapper->mapArticleData($article, $data); diff --git a/Application/MessageHandler/ModifyArticleMessageHandler.php b/Application/MessageHandler/ModifyArticleMessageHandler.php index 648dae6d..b13dff87 100644 --- a/Application/MessageHandler/ModifyArticleMessageHandler.php +++ b/Application/MessageHandler/ModifyArticleMessageHandler.php @@ -34,6 +34,9 @@ final class ModifyArticleMessageHandler */ private $articleMappers; + /** + * @param iterable $articleMappers + */ public function __construct( ArticleRepositoryInterface $articleRepository, iterable $articleMappers @@ -46,7 +49,7 @@ public function __invoke(ModifyArticleMessage $message): ArticleInterface { $identifier = $message->getIdentifier(); $data = $message->getData(); - $article = $this->articleRepository->findOneBy($identifier); + $article = $this->articleRepository->getOneBy($identifier); foreach ($this->articleMappers as $articleMapper) { $articleMapper->mapArticleData($article, $data); diff --git a/Common/MessageBus/Middleware/DoctrineFlushMiddleware.php b/Common/MessageBus/Middleware/DoctrineFlushMiddleware.php deleted file mode 100644 index 16252730..00000000 --- a/Common/MessageBus/Middleware/DoctrineFlushMiddleware.php +++ /dev/null @@ -1,64 +0,0 @@ -entityManager = $entityManager; - } - - public function handle(Envelope $envelope, StackInterface $stack): Envelope - { - ++$this->messageDepth; - - try { - $envelope = $stack->next()->handle($envelope, $stack); - } finally { - // need to decrease message depth in every case to start handling of next message at depth 0 - --$this->messageDepth; - } - - // flush unit-of-work to the database after the root message was handled successfully - if (0 === $this->messageDepth && !empty($envelope->all(EnableFlushStamp::class))) { - $this->entityManager->flush(); - } - - return $envelope; - } -} diff --git a/Common/MessageBus/Stamps/EnableFlushStamp.php b/Common/MessageBus/Stamps/EnableFlushStamp.php deleted file mode 100644 index c91387dc..00000000 --- a/Common/MessageBus/Stamps/EnableFlushStamp.php +++ /dev/null @@ -1,25 +0,0 @@ -getExtensionConfig('sulu_search'); foreach ($suluSearchConfigs as $suluSearchConfig) { - if (isset($suluSearchConfig['website']['indexes'])) { + if (isset($suluSearchConfig['website']['indexes'])) { // @phpstan-ignore-line $container->prependExtensionConfig( 'sulu_search', [ @@ -461,9 +461,28 @@ private function prependExperimentalStorage(ContainerBuilder $container): void } } + /** + * @param mixed[] $configs + */ public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); + /** + * @var array{ + * storage: 'experimental'|'phpcr', + * objects: array, + * default_main_webspace: string|null, + * default_additional_webspaces: string[]|null, + * types: array, + * display_tab_all: bool, + * smart_content: array{ + * default_limit: int, + * }, + * search_fields: string[], + * documents: array, + * default_author: bool, + * } $config + */ $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); @@ -478,12 +497,16 @@ public function load(array $configs, ContainerBuilder $container) } } + /** + * @param array{objects: array} $config + */ private function loadExperimentalStorage(array $config, ContainerBuilder $container, Loader\XmlFileLoader $loader): void { $this->configurePersistence($config['objects'], $container); $loader->load('experimental.xml'); - $this->createMessageBus($container, 'sulu_article.message_bus'); + + $container->setAlias('sulu_article.message_bus', 'sulu_message_bus'); $container->registerForAutoconfiguration(ArticleMapperInterface::class) ->addTag('sulu_article.article_mapper'); @@ -491,6 +514,19 @@ private function loadExperimentalStorage(array $config, ContainerBuilder $contai /** * Can be removed when phpcr storage is removed. + * + * @param array{ + * default_main_webspace: string|null, + * default_additional_webspaces: string[]|null, + * types: array, + * display_tab_all: bool, + * smart_content: array{ + * default_limit: int, + * }, + * search_fields: string[], + * documents: array, + * default_author: bool, + * } $config */ private function loadPHPCRStorage(array $config, ContainerBuilder $container, Loader\XmlFileLoader $loader): void { @@ -529,6 +565,8 @@ private function loadPHPCRStorage(array $config, ContainerBuilder $container, Lo * Append configuration for article "set_default_author". * * Can be removed when phpcr storage is removed. + * + * @param array{default_author: bool} $config */ private function appendDefaultAuthor(array $config, ContainerBuilder $container): void { @@ -575,33 +613,4 @@ private function cloneArticleConfig(array $config, string $type): array return $result; } - - private function createMessageBus(ContainerBuilder $container, string $busId): void - { - // We can not prepend the message bus in framework bundle as we don't - // want that it is accidentally the default bus of a project. - // So we create the bus here ourselves be reimplementing the logic of - // the FrameworkExtension. - // See: https://github.com/symfony/symfony/blob/v4.4.16/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L1735-L1774 - - $container->register($busId, MessageBus::class) - ->addArgument([]) - ->addTag('messenger.bus'); - - $middleware = [ - // before from: https://github.com/symfony/symfony/blob/v4.4.16/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L1736-L1741 - ['id' => 'add_bus_name_stamp_middleware', 'arguments' => [$busId]], - ['id' => 'reject_redelivered_message_middleware'], - ['id' => 'dispatch_after_current_bus'], - ['id' => 'failed_message_processing_middleware'], - // custom middlewares - ['id' => 'sulu_article.doctrine_flush_middleware'], - // after from: https://github.com/symfony/symfony/blob/v4.4.16/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L1742-L1745 - ['id' => 'send_message'], - ['id' => 'handle_message'], - ]; - - $container->setParameter($busId . '.middleware', $middleware); - $container->registerAliasForArgument($busId, MessageBusInterface::class); - } } diff --git a/Domain/Exception/ArticleNotFoundException.php b/Domain/Exception/ArticleNotFoundException.php index 92289e88..6ab5600d 100644 --- a/Domain/Exception/ArticleNotFoundException.php +++ b/Domain/Exception/ArticleNotFoundException.php @@ -21,7 +21,7 @@ class ArticleNotFoundException extends \Exception private $model; /** - * @param array $filters + * @var array */ private $filters; diff --git a/Infrastructure/Doctrine/Repository/ArticleRepository.php b/Infrastructure/Doctrine/Repository/ArticleRepository.php index 4dc9e6b2..607a601d 100644 --- a/Infrastructure/Doctrine/Repository/ArticleRepository.php +++ b/Infrastructure/Doctrine/Repository/ArticleRepository.php @@ -29,7 +29,7 @@ class ArticleRepository implements ArticleRepositoryInterface */ private const SELECTS = [ // GROUPS - self::SELECT_ARTICLE_CONTENT => [ + self::GROUP_SELECT_ARTICLE_ADMIN => [ self::SELECT_ARTICLE_CONTENT => [ DimensionContentQueryEnhancer::GROUP_SELECT_CONTENT_ADMIN => true, ], @@ -125,13 +125,13 @@ public function countBy(array $filters = []): int // instead of that the developer need to take that into account // in there call of the countBy method. unset($filters['page']); // @phpstan-ignore-line - unset($filters['limit']); + unset($filters['limit']); // @phpstan-ignore-line $queryBuilder = $this->createQueryBuilder($filters); $queryBuilder->select('COUNT(DISTINCT article.uuid)'); - return (int) $queryBuilder->getQuery()->getSingleScalarResult(); + return (int) $queryBuilder->getQuery()->getSingleScalarResult(); // @phpstan-ignore-line } /** @@ -243,7 +243,7 @@ private function createQueryBuilder(array $filters, array $sortBy = [], array $s // selects if ($selects[self::SELECT_ARTICLE_CONTENT] ?? null) { /** @var array $contentSelects */ - $contentSelects = $selects[self::SELECT_ARTICLE_CONTENT] ?? []; + $contentSelects = $selects[self::SELECT_ARTICLE_CONTENT]; $queryBuilder->leftJoin( 'article.dimensionContents', diff --git a/Resources/config/experimental.xml b/Resources/config/experimental.xml index cfa54d7f..62a9d06e 100644 --- a/Resources/config/experimental.xml +++ b/Resources/config/experimental.xml @@ -11,13 +11,6 @@ --> - - - - - - - diff --git a/Tests/Application/Kernel.php b/Tests/Application/Kernel.php index 1e66bbd7..de757a75 100644 --- a/Tests/Application/Kernel.php +++ b/Tests/Application/Kernel.php @@ -18,6 +18,7 @@ use Sulu\Bundle\HeadlessBundle\SuluHeadlessBundle; use Sulu\Bundle\TestBundle\Kernel\SuluTestKernel; use Sulu\Component\HttpKernel\SuluKernel; +use Sulu\Messenger\Infrastructure\Symfony\HttpKernel\SuluMessengerBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -56,6 +57,7 @@ public function registerBundles(): iterable if ('experimental_storage' === $this->config) { $bundles[] = new SuluContentBundle(); + $bundles[] = new SuluMessengerBundle(); } if ('extend' === \getenv('ARTICLE_TEST_CASE')) { diff --git a/UserInterface/Controller/Admin/ArticleController.php b/UserInterface/Controller/Admin/ArticleController.php index ef3ff299..3d08a7ec 100644 --- a/UserInterface/Controller/Admin/ArticleController.php +++ b/UserInterface/Controller/Admin/ArticleController.php @@ -20,7 +20,6 @@ use Sulu\Bundle\ArticleBundle\Application\Message\CreateArticleMessage; use Sulu\Bundle\ArticleBundle\Application\Message\ModifyArticleMessage; use Sulu\Bundle\ArticleBundle\Application\Message\RemoveArticleMessage; -use Sulu\Bundle\ArticleBundle\Common\MessageBus\Stamps\EnableFlushStamp; use Sulu\Bundle\ArticleBundle\Domain\Model\ArticleInterface; use Sulu\Bundle\ArticleBundle\Domain\Repository\ArticleRepositoryInterface; use Sulu\Bundle\ContentBundle\Content\Application\ContentManager\ContentManagerInterface; @@ -31,9 +30,11 @@ use Sulu\Component\Rest\ListBuilder\Metadata\FieldDescriptorFactoryInterface; use Sulu\Component\Rest\ListBuilder\PaginatedRepresentation; use Sulu\Component\Rest\RestHelperInterface; +use Sulu\Messenger\Infrastructure\Symfony\Messenger\FlushMiddleware\EnableFlushStamp; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\HandleTrait; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Stamp\HandledStamp; @@ -45,17 +46,13 @@ final class ArticleController implements ClassResourceInterface { use ControllerTrait; + use HandleTrait; /** * @var ArticleRepositoryInterface */ private $articleRepository; - /** - * @var MessageBusInterface - */ - private $messageBus; - /** * @var ContentManagerInterface */ @@ -125,15 +122,22 @@ public function cgetAction(Request $request): Response public function getAction(Request $request, string $id): Response // TODO route should be a uuid { $dimensionAttributes = [ - 'locale' => $request->query->get('locale', $request->getLocale()), + 'locale' => (string) $request->query->get('locale', $request->getLocale()), 'stage' => DimensionContentInterface::STAGE_DRAFT, ]; - $article = $this->articleRepository->getOneBy(\array_merge([ - 'uuid' => $id, - ], \array_replace($dimensionAttributes, ['loadGhost' => true])), [ - ArticleRepositoryInterface::GROUP_SELECT_ARTICLE_ADMIN, - ]); + $article = $this->articleRepository->getOneBy( + \array_merge( + [ + 'uuid' => $id, + 'load_ghost_content' => true, + ], + $dimensionAttributes, + ), + [ + ArticleRepositoryInterface::GROUP_SELECT_ARTICLE_ADMIN => true, + ] + ); // TODO the `$article` should just be serialized here with `['article_admin', 'content_admin']` // Instead of calling the content resolver service which triggers an additional query. @@ -151,6 +155,7 @@ public function postAction(Request $request): Response $message = new CreateArticleMessage($this->getData($request)); /** @see Sulu\Bundle\ArticleBundle\Application\MessageHandler\CreateArticleMessageHandler */ + /** @var ArticleInterface $article */ $article = $this->handle($message); $uuid = $article->getUuid(); @@ -172,7 +177,7 @@ public function putAction(Request $request, string $id): Response // TODO route return $this->getAction($request, $id); } - public function postTriggerAction(Request $request, $id): Response + public function postTriggerAction(Request $request, string $id): Response { $this->handleAction($request, $id); @@ -188,32 +193,6 @@ public function deleteAction(Request $request, string $id): Response // TODO rou return new Response('', 204); } - private function handle(object $message): ?ArticleInterface - { - try { - $envelope = $this->messageBus->dispatch($message, [new EnableFlushStamp()]); - } catch (HandlerFailedException $exception) { /** @phpstan-ignore-line */ // @codeCoverageIgnore - // @codeCoverageIgnoreStart - if ($previous = $exception->getPrevious()) { - throw $previous; - } - - throw $exception; - // @codeCoverageIgnoreEnd - } - - /** @var HandledStamp[] $handledStamps */ - $handledStamps = $envelope->all(HandledStamp::class); - - /** @var HandledStamp $handledStamp|null */ - $handledStamp = \reset($handledStamps); - - /** @var ArticleInterface|null $article */ - $article = $handledStamp ? $handledStamp->getResult() : null; - - return $article; - } - /** * @return mixed[] */ @@ -244,14 +223,16 @@ private function handleAction(Request $request, string $uuid): ?ArticleInterface /** @see Sulu\Bundle\ArticleBundle\Application\MessageHandler\CopyLocaleArticleMessageHandler */ $message = new CopyLocaleArticleMessage( ['uuid' => $uuid], - $request->query->get('src'), - $request->query->get('dest') + (string) $request->query->get('src'), + (string) $request->query->get('dest') ); /** @see Sulu\Bundle\ArticleBundle\Application\MessageHandler\CopyLocaleArticleMessageHandler */ + /** @var ArticleInterface */ return $this->handle($message); } else { $message = new ApplyWorkflowTransitionArticleMessage(['uuid' => $uuid], $this->getLocale($request), $action); /** @see Sulu\Bundle\ArticleBundle\Application\MessageHandler\ApplyWorkflowTransitionArticleMessageHandler */ + /** @var ArticleInterface */ return $this->handle($message); } } diff --git a/composer.json b/composer.json index 2e804312..96d7bfef 100644 --- a/composer.json +++ b/composer.json @@ -57,6 +57,7 @@ "sulu/automation-bundle": "^2.0@dev", "sulu/content-bundle": "0.*@dev", "sulu/headless-bundle": "^0.8.0@dev", + "sulu/messenger": "0.*@dev", "symfony/browser-kit": "^4.3 || ^5.0 || ^6.0", "symfony/dotenv": "^4.3 || ^5.0 || ^6.0", "symfony/framework-bundle": "^4.3 || ^5.0 || ^6.0", diff --git a/phpstan.neon b/phpstan.neon index 021ebb66..80a51e30 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -18,7 +18,7 @@ parameters: - %currentWorkingDirectory%/Tests/* - %currentWorkingDirectory%/Resources/phpcr-migrations/* symfony: - container_xml_path: %rootDir%/../../../Tests/Application/var/cache/admin/dev/Sulu_Bundle_ArticleBundle_Tests_Application_KernelDevDebugContainer.xml + container_xml_path: %currentWorkingDirectory%/Tests/Application/var/cache/admin/dev/phpcr_storage/Sulu_Bundle_ArticleBundle_Tests_Application_KernelDevDebugContainer.xml console_application_loader: Tests/phpstan/console-application.php doctrine: objectManagerLoader: Tests/phpstan/object-manager.php