diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 98f8c522..fb7f47a6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: jobs: tests: - name: ${{ matrix.php }} ${{ matrix.coverage }} ${{ matrix.engine }} ${{ matrix.solr_version }} ${{ matrix.regression }} ${{ matrix.core_setup }} + name: ${{ matrix.php }} ${{ matrix.coverage }} ${{ matrix.engine }} ${{ matrix.solr_version }} ${{ matrix.regression }} ${{ matrix.asynchronous }} ${{ matrix.core_setup }} runs-on: ubuntu-latest env: extensions: pdo_sqlite @@ -53,6 +53,37 @@ jobs: solr_cloud: 'yes' core_setup: 'cloud' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '7.7.3' + solr_cores: 'collection1' + core_setup: 'single' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '7.7.3' + core_setup: 'dedicated' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '7.7.3' + core_setup: 'shared' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '7.7.3' + solr_cloud: 'yes' + core_setup: 'cloud' + asynchronous: 'asynchronous' + - php: '8.1' coverage: 'integration' engine: 'solr' @@ -84,6 +115,41 @@ jobs: core_setup: 'cloud' regression: 'regression' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '7.7.3' + solr_cores: 'collection1' + core_setup: 'single' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '7.7.3' + core_setup: 'dedicated' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '7.7.3' + core_setup: 'shared' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '7.7.3' + solr_cloud: 'yes' + core_setup: 'cloud' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' coverage: 'integration' engine: 'solr' @@ -111,6 +177,37 @@ jobs: solr_cloud: 'yes' core_setup: 'cloud' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '8.11.2' + solr_cores: 'collection1' + core_setup: 'single' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '8.11.2' + core_setup: 'dedicated' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '8.11.2' + core_setup: 'shared' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-integration-solr-asynchronous.xml' + solr_version: '8.11.2' + solr_cloud: 'yes' + core_setup: 'cloud' + asynchronous: 'asynchronous' + - php: '8.1' coverage: 'integration' engine: 'solr' @@ -142,6 +239,41 @@ jobs: core_setup: 'cloud' regression: 'regression' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '8.11.2' + solr_cores: 'collection1' + core_setup: 'single' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '8.11.2' + core_setup: 'dedicated' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '8.11.2' + core_setup: 'shared' + regression: 'regression' + asynchronous: 'asynchronous' + - php: '8.1' + coverage: 'integration' + engine: 'solr' + config: 'phpunit-core-integration-legacy-solr-asynchronous.xml' + solr_version: '8.11.2' + solr_cloud: 'yes' + core_setup: 'cloud' + regression: 'regression' + asynchronous: 'asynchronous' + steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 diff --git a/bundle/DependencyInjection/Configuration.php b/bundle/DependencyInjection/Configuration.php index ce69b341..f8174832 100644 --- a/bundle/DependencyInjection/Configuration.php +++ b/bundle/DependencyInjection/Configuration.php @@ -7,6 +7,7 @@ use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; class Configuration implements ConfigurationInterface { @@ -25,6 +26,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addIndexableFieldTypeSection($rootNode); $this->addSearchResultExtractorSection($rootNode); $this->addAsynchronousIndexingSection($rootNode); + $this->addHierarchicalIndexingSection($rootNode); return $treeBuilder; } @@ -45,7 +47,7 @@ private function addIndexableFieldTypeSection(ArrayNodeDefinition $nodeDefinitio ->info("Maximum number of characters for the indexed short text ('value' string type field)") ->defaultValue(256) ->end() - ->end() + ?->end() ->end() ->end() ->end() @@ -60,7 +62,7 @@ private function addSearchResultExtractorSection(ArrayNodeDefinition $nodeDefini ->info('Get search result objects by loading them from the persistence layer, instead of reconstructing them from the returned Solr data') ->defaultTrue() ->end() - ->end(); + ?->end(); } private function addAsynchronousIndexingSection(ArrayNodeDefinition $nodeDefinition): void @@ -71,6 +73,110 @@ private function addAsynchronousIndexingSection(ArrayNodeDefinition $nodeDefinit ->info('Use asynchronous mechanism to handle repository content indexing') ->defaultFalse() ->end() - ->end(); + ?->end(); + } + + private function addHierarchicalIndexingSection(ArrayNodeDefinition $nodeDefinition): void + { + $childrenNodeDefinition = $nodeDefinition + ->children() + ->arrayNode('hierarchical_indexing') + ->info('Hierarchical indexing configuration') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('descendant_indexing') + ->info('Descendant indexing configuration') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled') + ->info('Enable/disable descendant indexing') + ->defaultFalse() + ->end() + ?->arrayNode('map') + ->useAttributeAsKey('name') + ->normalizeKeys(false) + ->arrayPrototype() + ->children() + ->arrayNode('handlers') + ->info('List of indexing handlers to execute') + ->example([ + 'handler_identifier_1', + 'handler_identifier_2', + ]) + ->scalarPrototype() + ->defaultValue([]) + ->validate() + ->ifTrue(fn ($v) => !is_string($v)) + ->thenInvalid('Handler identifier must be a string.') + ->end() + ->end() + ?->end() + ?->arrayNode('children') + ->useAttributeAsKey('name') + ->normalizeKeys(false) + ->arrayPrototype() + ; + + $this->buildChildrenNode($childrenNodeDefinition); + } + + private function evaluateChildren(&$child, $name): void + { + $builder = new TreeBuilder($name, 'array'); + $root = $builder->getRootNode(); + + $this->buildChildrenNode($root); + + $root->getNode(true)->finalize($child); + } + + private function buildChildrenNode(ArrayNodeDefinition $node): void + { + $node + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('indexed') + ->info('Whether the node should be indexed') + ->defaultTrue() + ->end() + ?->variableNode('children') + ->defaultValue([]) + ->validate() + ->ifTrue(fn ($v) => !is_array($v)) + ->thenInvalid('The children element must be an array.') + ->end() + ->validate() + ->always( + function ($children) { + array_walk($children, $this->evaluateChildren(...)); + + return $children; + } + ) + ->end() + ->end() + ?->end() + ->validate() + ->always( + function ($children) { + foreach (array_keys($children) as $key) { + $allowedOptions = ['indexed', 'children']; + + if (!in_array($key, $allowedOptions, true)) { + throw new InvalidConfigurationException( + sprintf( + 'Unrecognized option "%s". Available options are "%s".', + $key, + implode('", "', $allowedOptions), + ), + ); + } + } + + return $children; + } + ) + ->end() + ; } } diff --git a/bundle/DependencyInjection/NetgenIbexaSearchExtraExtension.php b/bundle/DependencyInjection/NetgenIbexaSearchExtraExtension.php index 729acae5..f13c81a4 100644 --- a/bundle/DependencyInjection/NetgenIbexaSearchExtraExtension.php +++ b/bundle/DependencyInjection/NetgenIbexaSearchExtraExtension.php @@ -88,6 +88,7 @@ private function processExtensionConfiguration(array $configs, ContainerBuilder $this->processIndexableFieldTypeConfiguration($configuration, $container); $this->processSearchResultExtractorConfiguration($configuration, $container); $this->processAsynchronousIndexingConfiguration($configuration, $container); + $this->processDescendantIndexingConfiguration($configuration, $container); } private function processSearchResultExtractorConfiguration(array $configuration, ContainerBuilder $container): void @@ -117,4 +118,12 @@ private function processAsynchronousIndexingConfiguration(array $configuration, $configuration['use_asynchronous_indexing'], ); } + + private function processDescendantIndexingConfiguration(array $configuration, ContainerBuilder $container): void + { + $container->setParameter( + 'netgen.ibexa_search_extra.descendant_indexing.configuration', + $configuration['hierarchical_indexing']['descendant_indexing'], + ); + } } diff --git a/bundle/NetgenIbexaSearchExtraBundle.php b/bundle/NetgenIbexaSearchExtraBundle.php index eb3dbec3..a283469a 100644 --- a/bundle/NetgenIbexaSearchExtraBundle.php +++ b/bundle/NetgenIbexaSearchExtraBundle.php @@ -5,6 +5,7 @@ namespace Netgen\Bundle\IbexaSearchExtraBundle; use Netgen\IbexaSearchExtra\Container\Compiler; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -21,6 +22,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new Compiler\AggregateFacetBuilderVisitorPass()); $container->addCompilerPass(new Compiler\AggregateSubdocumentQueryCriterionVisitorPass()); $container->addCompilerPass(new Compiler\AsynchronousIndexingPass()); + $container->addCompilerPass(new Compiler\DescendantIndexingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100); $container->addCompilerPass(new Compiler\FieldType\RichTextIndexablePass()); $container->addCompilerPass(new Compiler\SearchResultExtractorPass()); $container->addCompilerPass(new Compiler\RawFacetBuilderDomainVisitorPass()); diff --git a/docs/reference/descendant_indexing.rst b/docs/reference/descendant_indexing.rst new file mode 100644 index 00000000..639492f8 --- /dev/null +++ b/docs/reference/descendant_indexing.rst @@ -0,0 +1,71 @@ +Descendant indexing +===================== + +This feature helps in indexing hierarchical content structures. It allows the children of a content item to be indexed +within the same document as the parent if both are configured for descendant indexing. + +''Configuration'' + +To enable this feature, set up the descendant indexing configuration: + +.. code-block:: yaml + hierarchical_indexing: + descendant_indexing: + enabled: false + map: + content_type_identifier: + handlers: + - handler_identifier_1 + - handler_identifier_2 + children: + content_type_identifier: + indexed: true + +The ``enabled`` field must be set to true to activate descendant indexing services by registering them in the container. +In the array parameter ``map`` we define the structure of content to be included in descendant indexing by content types. +Any structure in the content tree that matches the configuration will be part of descendant indexing. Content can be +part of the structure but not included in the index. To index the content in the parent document, set the ``indexed``` +parameter to ``true``. + +This feature is automatically triggered during indexing when configured correctly. + +Depending on what we want to index, we use different handlers. They represent the field mappers used to index the content. +If you want to index content to the full text fields, you should use the 'ng_descendant_indexing_fulltext' handler: + +.. code-block:: yaml + hierarchical_indexing: + descendant_indexing: + enabled: true + map: + content_type_identifier: + handlers: + - ng_descendant_indexing_fulltext + children: + content_type_identifier: + indexed: true + +To index something other than full text fields (e.g., location information or content metadata), implement new field +mappers by extending the corresponding ``BaseFieldMapper`` and registering the field mapper as a service with needed tag. +The ``getIdentifier()`` method returns a string of handler identifier which should match the handler +identifier defined in the configuration. + +.. code-block:: php + public function getIdentifier(): string + { + return 'ng_descendant_indexing_fulltext'; + } + +''AncestorIndexer'' + +AncestorIndexer is a service that ensures descendant indexing is considered during reindexing. For example, if we edit +content that is part of the descendant indexing map, the descendant content in which it is indexed should also be +reindexed. + +The service contains methods ``indexSingle()`` and ``indexMultiple()``, which are called in handlers for any content +changes (e.g., ``CopyContentHandler``, ``DeleteContentHandler``). These methods use the AncestorResolver service to +resolve the ancestor to be reindexed. If no ancestor matches the configuration map structure, the ``resolveAncestor()`` +method returns null. + +The AncestorResolver service uses AncestorPathGenerator service to read from the configuration and return an array of +strings representing all of the paths matching the given configuration in order to be able to find a match with any of +the paths. diff --git a/lib/Container/Compiler/AsynchronousIndexingPass.php b/lib/Container/Compiler/AsynchronousIndexingPass.php index 87852388..82062a68 100644 --- a/lib/Container/Compiler/AsynchronousIndexingPass.php +++ b/lib/Container/Compiler/AsynchronousIndexingPass.php @@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; final class AsynchronousIndexingPass implements CompilerPassInterface { @@ -37,6 +38,7 @@ public function process(ContainerBuilder $container): void ->setDecoratedService(CoreContentEventSubscriber::class) ->setArguments([ new Reference('netgen.ibexa_search_extra.asynchronous_indexing.messenger.bus'), + new Reference(LocationHandler::class), ]); $container diff --git a/lib/Container/Compiler/DescendantIndexingPass.php b/lib/Container/Compiler/DescendantIndexingPass.php new file mode 100644 index 00000000..2bb1939b --- /dev/null +++ b/lib/Container/Compiler/DescendantIndexingPass.php @@ -0,0 +1,101 @@ +getParameter(self::DescendantIndexingConfigurationParameter); + $isEnabled = $configuration['enabled'] ?? false; + + if (!$isEnabled) { + return; + } + + $this->registerHandlers($container); + $this->registerSolrContentFieldMappers($container); + $this->registerSolrContentTranslationFieldMappers($container); + $this->registerSolrLocationFieldMappers($container); + } + + private function registerHandlers(ContainerBuilder $container): void + { + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingMessageHandlerTag); + + foreach ($serviceIds as $serviceId => $tag) { + $definition = $container->getDefinition($serviceId); + $definition->addTag(self::MessageHandlerTag); + } + } + + private function registerSolrBlockFieldMappers(ContainerBuilder $container): void + { + $definition = $container->getDefinition(self::DescendantIndexingSolrBlockFieldMapperServiceId); + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingSolrBlockFieldMapperTag); + + foreach (array_keys($serviceIds) as $id) { + $definition->addMethodCall('addFieldMapper', [new Reference($id)]); + } + } + + private function registerSolrBlockTranslationFieldMappers(ContainerBuilder $container): void + { + $definition = $container->getDefinition(self::DescendantIndexingSolrBlockTranslationFieldMapperServiceId); + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingSolrBlockTranslationFieldMapperTag); + + foreach (array_keys($serviceIds) as $id) { + $definition->addMethodCall('addFieldMapper', [new Reference($id)]); + } + } + + private function registerSolrContentFieldMappers(ContainerBuilder $container): void + { + $definition = $container->getDefinition(self::DescendantIndexingSolrContentFieldMapperServiceId); + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingSolrContentFieldMapperTag); + + foreach (array_keys($serviceIds) as $id) { + $definition->addMethodCall('addFieldMapper', [new Reference($id)]); + } + } + + private function registerSolrContentTranslationFieldMappers(ContainerBuilder $container): void + { + $definition = $container->getDefinition(self::DescendantIndexingSolrContentTranslationFieldMapperServiceId); + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingSolrContentTranslationFieldMapperTag); + + foreach (array_keys($serviceIds) as $id) { + $definition->addMethodCall('addFieldMapper', [new Reference($id)]); + } + } + + private function registerSolrLocationFieldMappers(ContainerBuilder $container): void + { + $definition = $container->getDefinition(self::DescendantIndexingSolrLocationFieldMapperServiceId); + $serviceIds = $container->findTaggedServiceIds(self::DescendantIndexingSolrLocationFieldMapperTag); + + foreach (array_keys($serviceIds) as $id) { + $definition->addMethodCall('addFieldMapper', [new Reference($id)]); + } + } +} diff --git a/lib/Core/Search/Common/EventSubscriber/ContentEventSubscriber.php b/lib/Core/Search/Common/EventSubscriber/ContentEventSubscriber.php index 418ad801..c584bcbf 100644 --- a/lib/Core/Search/Common/EventSubscriber/ContentEventSubscriber.php +++ b/lib/Core/Search/Common/EventSubscriber/ContentEventSubscriber.php @@ -12,6 +12,7 @@ use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent; use Ibexa\Contracts\Core\Repository\Events\Content\RevealContentEvent; use Ibexa\Contracts\Core\Repository\Events\Content\UpdateContentMetadataEvent; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\CopyContent; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteTranslation; @@ -25,8 +26,14 @@ class ContentEventSubscriber implements EventSubscriberInterface { + /** + * @var array + */ + private array $parentLocationIdsByContentId = []; + public function __construct( private readonly MessageBusInterface $messageBus, + private readonly LocationHandler $locationHandler, ) {} public static function getSubscribedEvents(): array @@ -55,8 +62,12 @@ public function onCopyContent(CopyContentEvent $event): void public function onBeforeDeleteContent(BeforeDeleteContentEvent $event): void { + $contentLocations = $this->locationHandler->loadLocationsByContent($event->getContentInfo()->id); + try { - $event->getContentInfo()->getMainLocation()?->parentLocationId; + foreach ($contentLocations as $contentLocation){ + $this->parentLocationIdsByContentId[$event->getContentInfo()->id][] = $contentLocation->parentId; + } } catch (Throwable) { // does nothing } @@ -64,19 +75,15 @@ public function onBeforeDeleteContent(BeforeDeleteContentEvent $event): void public function onDeleteContent(DeleteContentEvent $event): void { - try { - $mainLocationParentLocationId = $event->getContentInfo()->getMainLocation()?->parentLocationId; - } catch (Throwable) { - $mainLocationParentLocationId = null; - } - $this->messageBus->dispatch( new DeleteContent( $event->getContentInfo()->id, $event->getLocations(), - $mainLocationParentLocationId, + $this->parentLocationIdsByContentId[$event->getContentInfo()->id] ?? [], ), ); + + unset($this->parentLocationIdsByContentId[$event->getContentInfo()->id]); } public function onDeleteTranslation(DeleteTranslationEvent $event): void diff --git a/lib/Core/Search/Common/Messenger/Message/Search/Content/DeleteContent.php b/lib/Core/Search/Common/Messenger/Message/Search/Content/DeleteContent.php index f2d990c0..4c660d05 100644 --- a/lib/Core/Search/Common/Messenger/Message/Search/Content/DeleteContent.php +++ b/lib/Core/Search/Common/Messenger/Message/Search/Content/DeleteContent.php @@ -8,10 +8,11 @@ final class DeleteContent { /** * @param int[] $locationIds + * @param int[] $parentLocationIds */ public function __construct( public readonly int $contentId, public readonly array $locationIds, - public readonly ?int $mainLocationParentLocationId, + public readonly ?array $parentLocationIds, ) {} } diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/CopyContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/CopyContentHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/CopyContentHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/CopyContentHandler.php index 33dc0a93..c6427eee 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/CopyContentHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/CopyContentHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteContentHandler.php similarity index 93% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteContentHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteContentHandler.php index 399f53c2..b4b8aecd 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteContentHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteContentHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteTranslationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteTranslationHandler.php similarity index 98% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteTranslationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteTranslationHandler.php index 62f52a24..299b6110 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/DeleteTranslationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/DeleteTranslationHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/HideContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/HideContentHandler.php similarity index 88% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/HideContentHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/HideContentHandler.php index 6489a5e1..8045dfd8 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/HideContentHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/HideContentHandler.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\HideContent; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class HideContentHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/PublishVersionHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/PublishVersionHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/PublishVersionHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/PublishVersionHandler.php index 474afd75..d591a408 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/PublishVersionHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/PublishVersionHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/RevealContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/RevealContentHandler.php similarity index 89% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/RevealContentHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/RevealContentHandler.php index a6fe8a22..fa32eaf2 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/RevealContentHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/RevealContentHandler.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\RevealContent; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class RevealContentHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/UpdateContentMetadataHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/UpdateContentMetadataHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/UpdateContentMetadataHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/UpdateContentMetadataHandler.php index 8768fe5d..dee53f23 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Content/UpdateContentMetadataHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Content/UpdateContentMetadataHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content; use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/AssignSectionToSubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/AssignSectionToSubtreeHandler.php similarity index 84% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/AssignSectionToSubtreeHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/AssignSectionToSubtreeHandler.php index fccf2768..edca93ec 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/AssignSectionToSubtreeHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/AssignSectionToSubtreeHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\AssignSectionToSubtree; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class AssignSectionToSubtreeHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CopySubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CopySubtreeHandler.php similarity index 83% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CopySubtreeHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CopySubtreeHandler.php index 243699ff..f0de28b4 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CopySubtreeHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CopySubtreeHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\CopySubtree; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class CopySubtreeHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CreateLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CreateLocationHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CreateLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CreateLocationHandler.php index 47e01eaa..061fc5e0 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/CreateLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/CreateLocationHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/DeleteLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/DeleteLocationHandler.php similarity index 91% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/DeleteLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/DeleteLocationHandler.php index c803f5a3..bab2e58f 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/DeleteLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/DeleteLocationHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\DeleteLocation; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/HideLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/HideLocationHandler.php similarity index 83% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/HideLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/HideLocationHandler.php index 1fece102..cfee4244 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/HideLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/HideLocationHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\HideLocation; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class HideLocationHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/MoveSubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/MoveSubtreeHandler.php similarity index 83% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/MoveSubtreeHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/MoveSubtreeHandler.php index 5f9b346f..f0c6bd72 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/MoveSubtreeHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/MoveSubtreeHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\MoveSubtree; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class MoveSubtreeHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/SwapLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/SwapLocationHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/SwapLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/SwapLocationHandler.php index 0e6e6e15..77df1bf1 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/SwapLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/SwapLocationHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UnhideLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UnhideLocationHandler.php similarity index 83% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UnhideLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UnhideLocationHandler.php index 73605ba5..b5549b3d 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UnhideLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UnhideLocationHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\UnhideLocation; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class UnhideLocationHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UpdateLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UpdateLocationHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UpdateLocationHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UpdateLocationHandler.php index 503fb993..f93668ae 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Location/UpdateLocationHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Location/UpdateLocationHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/ObjectState/SetContentStateHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/ObjectState/SetContentStateHandler.php similarity index 96% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/ObjectState/SetContentStateHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/ObjectState/SetContentStateHandler.php index 17f077cb..df5f873f 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/ObjectState/SetContentStateHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/ObjectState/SetContentStateHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ObjectState; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\ObjectState; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Section/AssignSectionHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Section/AssignSectionHandler.php similarity index 96% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Section/AssignSectionHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Section/AssignSectionHandler.php index 235e1ae6..b3ee64e7 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Section/AssignSectionHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Section/AssignSectionHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Section; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Section; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/SubtreeIndexer.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/SubtreeIndexer.php similarity index 98% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/SubtreeIndexer.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/SubtreeIndexer.php index 960f1244..ea416bc7 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/SubtreeIndexer.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/SubtreeIndexer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/RecoverHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/RecoverHandler.php similarity index 83% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/RecoverHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/RecoverHandler.php index d79aa1df..cc885487 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/RecoverHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/RecoverHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Recover; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; final class RecoverHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/TrashHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/TrashHandler.php similarity index 93% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/TrashHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/TrashHandler.php index aea98080..89beb61b 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/Trash/TrashHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/Trash/TrashHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Trash; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/AssignUserToUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/AssignUserToUserGroupHandler.php similarity index 84% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/AssignUserToUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/AssignUserToUserGroupHandler.php index 0e88d390..9d1f3003 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/AssignUserToUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/AssignUserToUserGroupHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\AssignUserToUserGroup; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\UserContentWithLocationIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\UserContentWithLocationIndexer; final class AssignUserToUserGroupHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/BeforeUnAssignUserFromUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/BeforeUnAssignUserFromUserGroupHandler.php similarity index 94% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/BeforeUnAssignUserFromUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/BeforeUnAssignUserFromUserGroupHandler.php index deb4320a..a09491df 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/BeforeUnAssignUserFromUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/BeforeUnAssignUserFromUserGroupHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserGroupHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserGroupHandler.php index 2f33db78..f930b500 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserGroupHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserHandler.php index 0af00545..5eef6204 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/CreateUserHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/CreateUserHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserGroupHandler.php similarity index 93% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserGroupHandler.php index 765af82c..344ba767 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserGroupHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUserGroup; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserHandler.php similarity index 93% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserHandler.php index 366b84a7..aaec018d 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/DeleteUserHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/DeleteUserHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Search\Handler as SearchHandler; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUser; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/MoveUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/MoveUserGroupHandler.php similarity index 93% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/MoveUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/MoveUserGroupHandler.php index e4fafa54..34e81888 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/MoveUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/MoveUserGroupHandler.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\MoveUserGroup; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UnAssignUserFromUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UnAssignUserFromUserGroupHandler.php similarity index 84% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UnAssignUserFromUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UnAssignUserFromUserGroupHandler.php index 9c6eca0c..410fe233 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UnAssignUserFromUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UnAssignUserFromUserGroupHandler.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UnAssignUserFromUserGroup; -use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\UserContentWithLocationIndexer; +use Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\UserContentWithLocationIndexer; final class UnAssignUserFromUserGroupHandler { diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserGroupHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserGroupHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserGroupHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserGroupHandler.php index 97825e65..e10e5c5f 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserGroupHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserGroupHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserHandler.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserHandler.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserHandler.php index e64da2f2..06368606 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/User/UpdateUserHandler.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/User/UpdateUserHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/UserContentWithLocationIndexer.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/UserContentWithLocationIndexer.php similarity index 97% rename from lib/Core/Search/Common/Messenger/MessageHandler/Search/UserContentWithLocationIndexer.php rename to lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/UserContentWithLocationIndexer.php index 01bbd01b..1df88a27 100644 --- a/lib/Core/Search/Common/Messenger/MessageHandler/Search/UserContentWithLocationIndexer.php +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/AsynchronousIndexing/UserContentWithLocationIndexer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search; +namespace Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing; use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorIndexer.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorIndexer.php new file mode 100644 index 00000000..af3ba129 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorIndexer.php @@ -0,0 +1,101 @@ +ancestorResolver->resolveAncestor($location); + + if ($ancestor === null) { + return; + } + + try { + $content = $this->contentHandler->load($ancestor->contentId); + } catch (NotFoundException) { + return; + } + + $this->searchHandler->indexContent($content); + $this->searchHandler->indexLocation($ancestor); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + */ + public function indexSingleForSwapLocation(Location $location, Location $swappedLocation): void + { + $ancestor = $this->ancestorResolver->resolveAncestorForSwapLocation($location, $swappedLocation); + + if ($ancestor === null) { + return; + } + + try { + $content = $this->contentHandler->load($ancestor->contentId); + } catch (NotFoundException) { + return; + } + + $this->searchHandler->indexContent($content); + $this->searchHandler->indexLocation($ancestor); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + */ + public function indexSingleForParentLocation(Location $location): void + { + $this->indexSingle($location); + + $ancestor = $this->ancestorResolver->resolveAncestorForParentLocation($location); + + if ($ancestor === null) { + return; + } + + try { + $content = $this->contentHandler->load($ancestor->contentId); + } catch (NotFoundException) { + return; + } + + $this->searchHandler->indexContent($content); + $this->searchHandler->indexLocation($ancestor); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $locations + */ + public function indexMultiple(array $locations): void + { + foreach ($locations as $location) { + $this->indexSingle($location); + } + } + + /*** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $locations + */ + public function indexMultipleForParentLocation(array $locations): void + { + foreach ($locations as $location) { + $this->indexSingleForParentLocation($location); + } + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorPathGenerator.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorPathGenerator.php new file mode 100644 index 00000000..1ddeb47b --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorPathGenerator.php @@ -0,0 +1,143 @@ +paths === null) { + $normalizedConfiguration = $this->normalizeConfiguration($this->configuration['map'] ?? []); + $paths = $this->recursiveFlattenPaths($normalizedConfiguration); + $this->paths = $this->expandPaths($paths); + } + + return $this->paths; + } + + /** + * @param string[] $paths + * + * @return string[] + */ + private function expandPaths(array $paths): array + { + $expandedPathsGrouped = [[]]; + + foreach ($paths as $path) { + $expandedPathsGrouped[] = $this->recursiveExpandPath(explode('/', $path)); + } + + return array_merge(...$expandedPathsGrouped); + } + + /** + * @param string[] $pathElements + * + * @return string[] + */ + private function recursiveExpandPath(array $pathElements): array + { + $expandedPaths = []; + + if (count($pathElements) > 1) { + $path = implode('/', $pathElements); + array_shift($pathElements); + + $expandedPaths = [ + $path, + ...$expandedPaths, + ...$this->recursiveExpandPath($pathElements), + ]; + } + + return $expandedPaths; + } + + /** + * @param array $config + * + * @return string[] + */ + private function recursiveFlattenPaths(array $config, string $path = ''): array + { + $paths = []; + + foreach ($config as $key => $value) { + if (is_array($value) && count($value) > 0) { + $paths = [ + ...$paths, + ...$this->recursiveFlattenPaths($value, '/' . $key . $path), + ]; + + continue; + } + + $paths[] = $key . $path; + } + + return $paths; + } + + /** + * @param array $config + * + * @return array + */ + private function normalizeConfiguration(array $config): array + { + $normalizedConfig = []; + + foreach ($config as $key => $value) { + $normalizedConfig[$key] = $this->recursiveNormalizeConfiguration($value); + } + + return $normalizedConfig; + } + + /** + * @param array $config + * + * @return array + */ + private function recursiveNormalizeConfiguration(array $config): array + { + $normalizedConfig = []; + + foreach ($config as $key => $value) { + if ($key === 'indexed' || $key === 'handlers') { + continue; + } + + if ($key === 'children') { + return $this->recursiveNormalizeConfiguration($value); + } + + $normalizedConfig[$key] = $this->recursiveNormalizeConfiguration($value); + } + + return $normalizedConfig; + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorResolver.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorResolver.php new file mode 100644 index 00000000..4c01e63c --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/AncestorResolver.php @@ -0,0 +1,186 @@ + + */ + private array $contentIdContentTypeIdentifierCache = []; + + public function __construct( + private readonly ContentHandler $contentHandler, + private readonly ContentTypeHandler $contentTypeHandler, + private readonly LocationHandler $locationHandler, + private readonly AncestorPathGenerator $ancestorPathGenerator, + ) {} + + public function resolveAncestor(Location $location): ?Location + { + $ancestry = [$location]; + + do { + $match = $this->matchPath($ancestry); + + if ($match === true) { + return end($ancestry); + } + } while (is_bool($match) && $this->addToAncestry($ancestry)); + + return null; + } + + public function resolveAncestorForSwapLocation(Location $location, Location $swappedLocation): ?Location + { + $contentInfo = $this->contentHandler->loadContentInfo($swappedLocation->contentId); + $contentType = $this->contentTypeHandler->load($contentInfo->contentTypeId); + $contentTypeIdentifier = $contentType->identifier; + $ancestry = [$location]; + + do { + $match = $this->matchPath($ancestry, $contentTypeIdentifier); + + if ($match === true) { + return end($ancestry); + } + } while (is_bool($match) && $this->addToAncestry($ancestry)); + + return null; + } + + /** + * Return the Location if its content type matches the path parent. + */ + public function resolveAncestorForParentLocation(Location $location): ?Location + { + try { + $contentTypeIdentifier = $this->getContentTypeIdentifier($location); + } catch (NotFoundException) { + return null; + } + + foreach ($this->ancestorPathGenerator->getPaths() as $path) { + if (str_ends_with($path, $contentTypeIdentifier)) { + return $location; + } + } + + return null; + } + + /** + * Return remaining string length if the path matches (if zero, the match is complete), false otherwise. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $ancestry + */ + private function matchPath(array $ancestry, ?string $firstContentTypeIdentifier = null): ?bool + { + $ancestryPath = $this->getAncestryPath($ancestry, $firstContentTypeIdentifier); + + if ($ancestryPath === null) { + return false; + } + $isPartialMatch = false; + + foreach ($this->ancestorPathGenerator->getPaths() as $path) { + if (str_starts_with($path, $ancestryPath . '/')) { + $isPartialMatch = true; + } + + if ($path === $ancestryPath) { + return true; + } + } + + if ($isPartialMatch) { + return false; + } + + return null; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $ancestry + */ + private function getAncestryPath(array $ancestry, ?string $firstContentTypeIdentifier = null): ?string + { + $pathElements = []; + + foreach ($ancestry as $index => $location) { + try { + if ($index === 0 && $firstContentTypeIdentifier !== null) { + $pathElements[] = $firstContentTypeIdentifier; + } else { + $pathElements[] = $this->getContentTypeIdentifier($location); + } + } catch (NotFoundException) { + return null; + } + } + + return implode('/', $pathElements); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location[] $ancestry + */ + private function addToAncestry(array &$ancestry): bool + { + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location $last */ + $last = end($ancestry); + + if ($last->depth <= 1) { + return false; + } + + try { + $ancestry[] = $this->getParentLocation($last); + } catch (NotFoundException) { + return false; + } + + return true; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getParentLocation(Location $location): Location + { + return $this->locationHandler->load($location->parentId); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getContentTypeIdentifier(Location $location): string + { + /** @var int $contentId */ + $contentId = $location->contentId; + + if (!isset($this->contentIdContentTypeIdentifierCache[$contentId])) { + $contentInfo = $this->contentHandler->loadContentInfo($contentId); + $contentTypeId = $contentInfo->contentTypeId; + $contentType = $this->contentTypeHandler->load($contentTypeId); + + $this->contentIdContentTypeIdentifierCache[$contentId] = $contentType->identifier; + } + + return $this->contentIdContentTypeIdentifierCache[$contentId]; + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/CopyContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/CopyContentHandler.php new file mode 100644 index 00000000..be51305e --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/CopyContentHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteContentHandler.php new file mode 100644 index 00000000..0eb968a9 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteContentHandler.php @@ -0,0 +1,56 @@ +parentLocationIds) === 0) { + $this->logger->info( + sprintf( + '%s: Could not find parent Location IDs for deleted Content #%d, aborting', + $this::class, + $message->contentId, + ), + ); + + return; + } + + $locations = []; + + foreach ($message->parentLocationIds as $locationId) { + try { + $locations[] = $this->locationHandler->load($locationId); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $locationId, + ), + ); + } + } + + $this->ancestorIndexer->indexMultipleForParentLocation($locations); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteTranslationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteTranslationHandler.php new file mode 100644 index 00000000..49fcfd91 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/DeleteTranslationHandler.php @@ -0,0 +1,55 @@ +contentHandler->loadContentInfo( + $message->contentId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Content #%d is gone, aborting', + $this::class, + $message->contentId, + ), + ); + + return; + } + + if ($contentInfo->status !== ContentInfo::STATUS_PUBLISHED) { + return; + } + + $this->ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/HideContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/HideContentHandler.php new file mode 100644 index 00000000..7547fb57 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/HideContentHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/PublishVersionHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/PublishVersionHandler.php new file mode 100644 index 00000000..cdbe3b19 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/PublishVersionHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/RevealContentHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/RevealContentHandler.php new file mode 100644 index 00000000..8bbf039e --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/RevealContentHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/UpdateContentMetadataHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/UpdateContentMetadataHandler.php new file mode 100644 index 00000000..9594501b --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Content/UpdateContentMetadataHandler.php @@ -0,0 +1,67 @@ +contentHandler->loadContentInfo( + $message->contentId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Content #%d is gone, aborting', + $this::class, + $message->contentId, + ), + ); + + return; + } + + if ($contentInfo->status !== ContentInfo::STATUS_PUBLISHED) { + return; + } + + try { + $location = $this->locationHandler->load( + $contentInfo->mainLocationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $contentInfo->mainLocationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/AssignSectionToSubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/AssignSectionToSubtreeHandler.php new file mode 100644 index 00000000..ea3555ca --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/AssignSectionToSubtreeHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->locationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CopySubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CopySubtreeHandler.php new file mode 100644 index 00000000..248ac6d1 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CopySubtreeHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->locationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CreateLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CreateLocationHandler.php new file mode 100644 index 00000000..9da02cd7 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/CreateLocationHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->locationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/DeleteLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/DeleteLocationHandler.php new file mode 100644 index 00000000..d4acbcb1 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/DeleteLocationHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->parentLocationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->parentLocationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/HideLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/HideLocationHandler.php new file mode 100644 index 00000000..6667ab9c --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/HideLocationHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->locationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/MoveSubtreeHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/MoveSubtreeHandler.php new file mode 100644 index 00000000..25bb35b5 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/MoveSubtreeHandler.php @@ -0,0 +1,58 @@ +locationHandler->load( + $message->locationId, + ); + + $this->ancestorIndexer->indexSingle($location); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + } + + try { + $location = $this->locationHandler->load( + $message->oldParentLocationId, + ); + + $this->ancestorIndexer->indexSingleForParentLocation($location); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Old parent Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + } + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/SwapLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/SwapLocationHandler.php new file mode 100644 index 00000000..df50b30c --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/SwapLocationHandler.php @@ -0,0 +1,62 @@ +reindexForLocation($message->location1Id, $message->location2Id); + $this->reindexForLocation($message->location2Id, $message->location1Id); + } + + private function reindexForLocation(int $locationId, int $swappedLocationId): void + { + try { + $location = $this->locationHandler->load($locationId); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $locationId, + ), + ); + + return; + } + + try { + $swappedLocation = $this->locationHandler->load($swappedLocationId); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $swappedLocationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingleForSwapLocation($location, $swappedLocation); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UnhideLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UnhideLocationHandler.php new file mode 100644 index 00000000..06df3be7 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UnhideLocationHandler.php @@ -0,0 +1,44 @@ +locationHandler->load( + $message->locationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UpdateLocationHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UpdateLocationHandler.php new file mode 100644 index 00000000..0f19d78e --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Location/UpdateLocationHandler.php @@ -0,0 +1,42 @@ +locationHandler->load($message->locationId); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/ObjectState/SetContentStateHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/ObjectState/SetContentStateHandler.php new file mode 100644 index 00000000..bda806ba --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/ObjectState/SetContentStateHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Section/AssignSectionHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Section/AssignSectionHandler.php new file mode 100644 index 00000000..30adea80 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Section/AssignSectionHandler.php @@ -0,0 +1,26 @@ +ancestorIndexer->indexMultiple( + $this->locationHandler->loadLocationsByContent( + $message->contentId, + ), + ); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/RecoverHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/RecoverHandler.php new file mode 100644 index 00000000..daf94c13 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/RecoverHandler.php @@ -0,0 +1,42 @@ +locationHandler->load($message->locationId); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->locationId, + ), + ); + + return; + } + + $this->ancestorIndexer->indexSingle($location); + } +} diff --git a/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/TrashHandler.php b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/TrashHandler.php new file mode 100644 index 00000000..5a994bf6 --- /dev/null +++ b/lib/Core/Search/Common/Messenger/MessageHandler/Search/DescendantIndexing/Trash/TrashHandler.php @@ -0,0 +1,54 @@ +locationHandler->load( + $message->parentLocationId, + ); + } catch (NotFoundException) { + $this->logger->info( + sprintf( + '%s: Location #%d is gone, aborting', + $this::class, + $message->parentLocationId, + ), + ); + + return; + } + + if ($this->isRootLocation($location)) { + return; + } + + $this->ancestorIndexer->indexSingleForParentLocation($location); + } + + private function isRootLocation(Location $location): bool + { + return $location->contentId === 0; + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper.php new file mode 100644 index 00000000..7c169f53 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper.php @@ -0,0 +1,52 @@ +fieldMappers as $fieldMapper) { + $this->addFieldMapper($fieldMapper); + } + } + + public function addFieldMapper(BaseFieldMapper $fieldMapper): void + { + $this->fieldMappers[] = $fieldMapper; + } + + public function accept(Content $content): bool + { + return $this->configuration['enabled'] ?? false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function mapFields(Content $content): array + { + $fieldsGrouped = [[]]; + + foreach ($this->fieldMappers as $fieldMapper) { + if ($fieldMapper->accept($content)) { + $fieldsGrouped[] = $fieldMapper->mapFields($content); + } + } + + return array_merge(...$fieldsGrouped); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper/BaseFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper/BaseFieldMapper.php new file mode 100644 index 00000000..ed953b81 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Block/DescendantFieldMapper/BaseFieldMapper.php @@ -0,0 +1,43 @@ +internalAccept($content) && $this->doAccept($content); + } + + abstract public function doAccept(Content $content): bool; + + abstract public function getIdentifier(): string; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function internalAccept(Content $content): bool + { + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + + $map = $this->configuration['map'] ?? []; + $handlers = $map[$contentType->identifier]['handlers'] ?? []; + + return array_key_exists($contentType->identifier, $map) + && in_array($this->getIdentifier(), $handlers, true); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper.php new file mode 100644 index 00000000..645d6805 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper.php @@ -0,0 +1,52 @@ +fieldMappers as $fieldMapper) { + $this->addFieldMapper($fieldMapper); + } + } + + public function addFieldMapper(BaseFieldMapper $fieldMapper): void + { + $this->fieldMappers[] = $fieldMapper; + } + + public function accept(Content $content, $languageCode): bool + { + return $this->configuration['enabled'] ?? false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function mapFields(Content $content, $languageCode): array + { + $fieldsGrouped = [[]]; + + foreach ($this->fieldMappers as $fieldMapper) { + if ($fieldMapper->accept($content, $languageCode)) { + $fieldsGrouped[] = $fieldMapper->mapFields($content, $languageCode); + } + } + + return array_merge(...$fieldsGrouped); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper/BaseFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper/BaseFieldMapper.php new file mode 100644 index 00000000..cfa63079 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/BlockTranslation/DescendantFieldMapper/BaseFieldMapper.php @@ -0,0 +1,43 @@ +internalAccept($content) && $this->doAccept($content, $languageCode); + } + + abstract public function doAccept(Content $content, string $languageCode): bool; + + abstract public function getIdentifier(): string; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function internalAccept(Content $content): bool + { + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + + $map = $this->configuration['map'] ?? []; + $handlers = $map[$contentType->identifier]['handlers'] ?? []; + + return array_key_exists($contentType->identifier, $map) + && in_array($this->getIdentifier(), $handlers, true); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper.php new file mode 100644 index 00000000..51cd53eb --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper.php @@ -0,0 +1,52 @@ +fieldMappers as $fieldMapper) { + $this->addFieldMapper($fieldMapper); + } + } + + public function addFieldMapper(BaseFieldMapper $fieldMapper): void + { + $this->fieldMappers[] = $fieldMapper; + } + + public function accept(Content $content): bool + { + return $this->configuration['enabled'] ?? false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function mapFields(Content $content): array + { + $fieldsGrouped = [[]]; + + foreach ($this->fieldMappers as $fieldMapper) { + if ($fieldMapper->accept($content)) { + $fieldsGrouped[] = $fieldMapper->mapFields($content); + } + } + + return array_merge(...$fieldsGrouped); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper/BaseFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper/BaseFieldMapper.php new file mode 100644 index 00000000..3ea49a23 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Content/DescendantFieldMapper/BaseFieldMapper.php @@ -0,0 +1,43 @@ +internalAccept($content) && $this->doAccept($content); + } + + abstract public function doAccept(Content $content): bool; + + abstract public function getIdentifier(): string; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function internalAccept(Content $content): bool + { + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + + $map = $this->configuration['map'] ?? []; + $handlers = $map[$contentType->identifier]['handlers'] ?? []; + + return array_key_exists($contentType->identifier, $map) + && in_array($this->getIdentifier(), $handlers, true); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper.php new file mode 100644 index 00000000..aec82d0b --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper.php @@ -0,0 +1,52 @@ +fieldMappers as $fieldMapper) { + $this->addFieldMapper($fieldMapper); + } + } + + public function addFieldMapper(BaseFieldMapper $fieldMapper): void + { + $this->fieldMappers[] = $fieldMapper; + } + + public function accept(Content $content, $languageCode): bool + { + return $this->configuration['enabled'] ?? false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function mapFields(Content $content, $languageCode): array + { + $fieldsGrouped = [[]]; + + foreach ($this->fieldMappers as $fieldMapper) { + if ($fieldMapper->accept($content, $languageCode)) { + $fieldsGrouped[] = $fieldMapper->mapFields($content, $languageCode); + } + } + + return array_merge(...$fieldsGrouped); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/BaseFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/BaseFieldMapper.php new file mode 100644 index 00000000..48df3b33 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/BaseFieldMapper.php @@ -0,0 +1,44 @@ +internalAccept($content) && $this->doAccept($content, $languageCode); + } + + abstract public function doAccept(Content $content, string $languageCode): bool; + + abstract public function getIdentifier(): string; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function internalAccept(Content $content): bool + { + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + + $map = $this->configuration['map'] ?? []; + + $handlers = $map[$contentType->identifier]['handlers'] ?? []; + + return array_key_exists($contentType->identifier, $map) + && in_array($this->getIdentifier(), $handlers, true); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldMapper.php new file mode 100644 index 00000000..1632a4d7 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldMapper.php @@ -0,0 +1,193 @@ + + */ + private array $contentTypeIdIdentifierCache; + + /** + * @param array $configuration + */ + public function __construct( + private readonly FullTextFieldResolver $fullTextFieldResolver, + private readonly ContentTypeHandler $contentTypeHandler, + private readonly ContentHandler $contentHandler, + private readonly Handler $contentFilteringHandler, + private readonly LocationHandler $locationHandler, + private readonly array $configuration, + private readonly int $childrenLimit = 99, + ) { + parent::__construct( + $contentTypeHandler, + $this->configuration, + ); + } + + public function doAccept(SPIContent $content, $languageCode): bool + { + return true; + } + + /** + * @param string $languageCode + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function mapFields(SPIContent $content, $languageCode): array + { + $contentTypeId = $content->versionInfo->contentInfo->contentTypeId; + $contentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + return $this->recursiveMapFields( + $content->versionInfo->contentInfo, + $languageCode, + $this->configuration['map'][$contentTypeIdentifier] ?? [], + false, + ); + } + + public function getIdentifier(): string + { + return 'ng_descendant_indexing_fulltext'; + } + + /** + * @param array|null $configuration + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + private function recursiveMapFields( + ContentInfo $contentInfo, + string $languageCode, + ?array $configuration, + bool $doIndex = true, + ): array { + $fieldsGrouped = [[]]; + $isIndexed = !isset($configuration['indexed']) || $configuration['indexed']; + $childrenConfiguration = $configuration['children'] ?? []; + + if ($isIndexed && $doIndex) { + $content = $this->contentHandler->load($contentInfo->id); + $fieldsGrouped[] = $this->fullTextFieldResolver->resolveFields($content, $languageCode); + } + + $childrenContentInfoList = $this->loadChildrenContentInfoList( + $contentInfo, + $childrenConfiguration, + ); + + foreach ($childrenContentInfoList as $childContentInfo) { + $contentTypeId = $childContentInfo->contentTypeId; + $contentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + $childConfiguration = $childrenConfiguration[$contentTypeIdentifier] ?? []; + + $fieldsGrouped[] = $this->recursiveMapFields( + $childContentInfo, + $languageCode, + $childConfiguration, + ); + } + + return array_merge(...$fieldsGrouped); + } + + private function getContentTypeIdentifier(int $contentTypeId): ?string + { + if (isset($this->contentTypeIdIdentifierCache[$contentTypeId])) { + return $this->contentTypeIdIdentifierCache[$contentTypeId]; + } + + try { + $contentType = $this->contentTypeHandler->load($contentTypeId); + $identifier = $contentType->identifier; + } catch (NotFoundException) { + $identifier = null; + } + + $this->contentTypeIdIdentifierCache[$contentTypeId] = $identifier; + + return $identifier; + } + + /** + * @param array $configuration + * + * @throws BadStateException + * @throws InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo[] + */ + private function loadChildrenContentInfoList( + ContentInfo $contentInfo, + array $configuration, + ): array { + $contentTypeIdentifiers = array_keys($configuration); + + if (count($contentTypeIdentifiers) === 0) { + return []; + } + + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($contentTypeIdentifiers), + new ParentLocationId($contentInfo->mainLocationId), + ]) + ) + ->withLimit($this->childrenLimit); + + $contentItemList = $this->contentFilteringHandler->find($filter); + $items = []; + + foreach ($contentItemList as $contentItem) { + $contentLocations = $this->locationHandler->loadLocationsByContent($contentItem->contentInfo->id); + + foreach ($contentLocations as $contentLocation) { + if ( + $contentLocation->parentId === $contentInfo->mainLocationId + && !$contentLocation->hidden + && !$contentLocation->invisible + && !$contentInfo->isHidden + ) { + $items[] = $contentItem->contentInfo; + + break; + } + } + } + + return $items; + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldResolver.php b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldResolver.php new file mode 100644 index 00000000..46d82bea --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/ContentTranslation/DescendantFieldMapper/FullTextFieldResolver.php @@ -0,0 +1,15 @@ +versionInfo->contentInfo->contentTypeId; + $contentType = $this->contentTypeHandler->load($contentTypeId); + } catch (NotFoundException) { + return $fields; + } + + foreach ($content->fields as $field) { + if ($field->languageCode !== $languageCode) { + continue; + } + + foreach ($contentType->fieldDefinitions as $fieldDefinition) { + if (!$fieldDefinition->isSearchable) { + continue; + } + + if ($fieldDefinition->id !== $field->fieldDefinitionId) { + continue; + } + + $fieldType = $this->fieldRegistry->getType($field->type); + $indexFields = $fieldType->getIndexData($field, $fieldDefinition); + + foreach ($indexFields as $indexField) { + if ($indexField->getValue() === null) { + continue; + } + + if (!$indexField->getType() instanceof FullTextField) { + continue; + } + + $fields[] = new Field( + 'meta_content__text', + (string) $indexField->getValue(), + new TextField(), + ); + } + } + } + + return $fields; + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper.php new file mode 100644 index 00000000..590d3dbf --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper.php @@ -0,0 +1,52 @@ +fieldMappers as $fieldMapper) { + $this->addFieldMapper($fieldMapper); + } + } + + public function addFieldMapper(BaseFieldMapper $fieldMapper): void + { + $this->fieldMappers[] = $fieldMapper; + } + + public function accept(Location $location): bool + { + return $this->configuration['enabled'] ?? false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function mapFields(Location $location): array + { + $fieldsGrouped = [[]]; + + foreach ($this->fieldMappers as $fieldMapper) { + if ($fieldMapper->accept($location)) { + $fieldsGrouped[] = $fieldMapper->mapFields($location); + } + } + + return array_merge(...$fieldsGrouped); + } +} diff --git a/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper/BaseFieldMapper.php b/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper/BaseFieldMapper.php new file mode 100644 index 00000000..2468e795 --- /dev/null +++ b/lib/Core/Search/Solr/FieldMapper/Location/DescendantFieldMapper/BaseFieldMapper.php @@ -0,0 +1,46 @@ +internalAccept($location) && $this->doAccept($location); + } + + abstract public function doAccept(Location $location): bool; + + abstract public function getIdentifier(): string; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function internalAccept(Location $location): bool + { + $contentInfo = $this->contentHandler->loadContentInfo($location->contentId); + $contentType = $this->contentTypeHandler->load($contentInfo->contentTypeId); + + $map = $this->configuration['map'] ?? []; + $handlers = $map[$contentType->identifier]['handlers'] ?? []; + + return array_key_exists($contentType->identifier, $map) + && in_array($this->getIdentifier(), $handlers, true); + } +} diff --git a/lib/Resources/config/search/common.yaml b/lib/Resources/config/search/common.yaml index f8c994e9..656f8403 100644 --- a/lib/Resources/config/search/common.yaml +++ b/lib/Resources/config/search/common.yaml @@ -1,3 +1,4 @@ imports: - { resource: common/asynchronous_indexing.yaml } - { resource: common/field_value_mappers.yaml } + - { resource: common/descendant_indexing.yaml } diff --git a/lib/Resources/config/search/common/asynchronous_indexing/common.yaml b/lib/Resources/config/search/common/asynchronous_indexing/common.yaml index b02b4075..a3182174 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/common.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/common.yaml @@ -1,5 +1,5 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -8,7 +8,7 @@ services: tags: - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\UserContentWithLocationIndexer: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\UserContentWithLocationIndexer: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' diff --git a/lib/Resources/config/search/common/asynchronous_indexing/content.yaml b/lib/Resources/config/search/common/asynchronous_indexing/content.yaml index ea303fb8..437907f0 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/content.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/content.yaml @@ -1,5 +1,5 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\CopyContentHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\CopyContentHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -9,13 +9,13 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\DeleteContentHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\DeleteContentHandler: arguments: - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\DeleteTranslationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\DeleteTranslationHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -25,14 +25,14 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\HideContentHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\HideContentHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\PublishVersionHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\PublishVersionHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -42,14 +42,14 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\RevealContentHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\RevealContentHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\UpdateContentMetadataHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\UpdateContentMetadataHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' diff --git a/lib/Resources/config/search/common/asynchronous_indexing/location.yaml b/lib/Resources/config/search/common/asynchronous_indexing/location.yaml index 287eb478..6aef2ddd 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/location.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/location.yaml @@ -1,17 +1,17 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\AssignSectionToSubtreeHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\AssignSectionToSubtreeHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\CopySubtreeHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\CopySubtreeHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\CreateLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\CreateLocationHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -21,25 +21,25 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\DeleteLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\DeleteLocationHandler: arguments: - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\HideLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\HideLocationHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\MoveSubtreeHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\MoveSubtreeHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\SwapLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\SwapLocationHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -49,14 +49,14 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\UnhideLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\UnhideLocationHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\UpdateLocationHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\UpdateLocationHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' diff --git a/lib/Resources/config/search/common/asynchronous_indexing/object_state.yaml b/lib/Resources/config/search/common/asynchronous_indexing/object_state.yaml index b0b32e7c..fd73ad00 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/object_state.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/object_state.yaml @@ -1,5 +1,5 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ObjectState\SetContentStateHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\ObjectState\SetContentStateHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' diff --git a/lib/Resources/config/search/common/asynchronous_indexing/section.yaml b/lib/Resources/config/search/common/asynchronous_indexing/section.yaml index 8a7c330e..af971a52 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/section.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/section.yaml @@ -1,5 +1,5 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Section\AssignSectionHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Section\AssignSectionHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@ibexa.spi.search' diff --git a/lib/Resources/config/search/common/asynchronous_indexing/trash.yaml b/lib/Resources/config/search/common/asynchronous_indexing/trash.yaml index fc3d7813..15f22452 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/trash.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/trash.yaml @@ -1,12 +1,12 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash\RecoverHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash\RecoverHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash\TrashHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash\TrashHandler: arguments: - '@ibexa.spi.search' tags: diff --git a/lib/Resources/config/search/common/asynchronous_indexing/user.yaml b/lib/Resources/config/search/common/asynchronous_indexing/user.yaml index ca054664..6b2a095c 100644 --- a/lib/Resources/config/search/common/asynchronous_indexing/user.yaml +++ b/lib/Resources/config/search/common/asynchronous_indexing/user.yaml @@ -1,18 +1,18 @@ services: - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\AssignUserToUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\AssignUserToUserGroupHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\UserContentWithLocationIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\UserContentWithLocationIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\BeforeUnAssignUserFromUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\BeforeUnAssignUserFromUserGroupHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\CreateUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\CreateUserGroupHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -21,7 +21,7 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\CreateUserHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\CreateUserHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -31,32 +31,32 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\DeleteUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\DeleteUserGroupHandler: arguments: - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\DeleteUserHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\DeleteUserHandler: arguments: - '@ibexa.spi.search' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\MoveUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\MoveUserGroupHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\SubtreeIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\SubtreeIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UnAssignUserFromUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UnAssignUserFromUserGroupHandler: arguments: - - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\UserContentWithLocationIndexer' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\UserContentWithLocationIndexer' tags: - { name: messenger.message_handler } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UpdateUserGroupHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UpdateUserGroupHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' @@ -66,7 +66,7 @@ services: - { name: messenger.message_handler } - { name: monolog.logger, channel: ngsearchextra } - Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UpdateUserHandler: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UpdateUserHandler: arguments: - '@Ibexa\Contracts\Core\Persistence\Content\Handler' - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' diff --git a/lib/Resources/config/search/common/descendant_indexing.yaml b/lib/Resources/config/search/common/descendant_indexing.yaml new file mode 100644 index 00000000..ca4dc40f --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing.yaml @@ -0,0 +1,10 @@ +imports: + - { resource: descendant_indexing/common.yaml } + - { resource: descendant_indexing/content.yaml } + - { resource: descendant_indexing/location.yaml } + - { resource: descendant_indexing/object_state.yaml } + - { resource: descendant_indexing/section.yaml } + - { resource: descendant_indexing/trash.yaml } + +parameters: + netgen.ibexa_search_extra.descendant_indexing.configuration: [] diff --git a/lib/Resources/config/search/common/descendant_indexing/common.yaml b/lib/Resources/config/search/common/descendant_indexing/common.yaml new file mode 100644 index 00000000..7fdb828f --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/common.yaml @@ -0,0 +1,20 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.ancestor_path_generator: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\AncestorPathGenerator + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + + netgen.ibexa_search_extra.descendant_indexing.ancestor_resolver: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\AncestorResolver + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + - '@Ibexa\Core\Persistence\Cache\LocationHandler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_path_generator' + + netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\AncestorIndexer + arguments: + - '@Ibexa\Contracts\Core\Search\VersatileHandler' + - '@Ibexa\Core\Persistence\Legacy\Content\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_resolver' diff --git a/lib/Resources/config/search/common/descendant_indexing/content.yaml b/lib/Resources/config/search/common/descendant_indexing/content.yaml new file mode 100644 index 00000000..50f1eb57 --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/content.yaml @@ -0,0 +1,64 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.message_handler.copy_content: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\CopyContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.delete_content: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.delete_translation: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteTranslationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.hide_content: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\HideContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.publish_version: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\PublishVersionHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.reveal_content: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\RevealContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.update_content_metadata: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\UpdateContentMetadataHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } diff --git a/lib/Resources/config/search/common/descendant_indexing/location.yaml b/lib/Resources/config/search/common/descendant_indexing/location.yaml new file mode 100644 index 00000000..dab92d9d --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/location.yaml @@ -0,0 +1,90 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.message_handler.assign_section_to_subtree: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\AssignSectionToSubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.copy_subtree: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CopySubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.create_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CreateLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.delete_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\DeleteLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.hide_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\HideLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.move_subtree: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\MoveSubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.swap_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\SwapLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.unhide_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UnhideLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.update_location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UpdateLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } diff --git a/lib/Resources/config/search/common/descendant_indexing/object_state.yaml b/lib/Resources/config/search/common/descendant_indexing/object_state.yaml new file mode 100644 index 00000000..bf196e77 --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/object_state.yaml @@ -0,0 +1,8 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.message_handler.set_content_state: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\ObjectState\SetContentStateHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } diff --git a/lib/Resources/config/search/common/descendant_indexing/section.yaml b/lib/Resources/config/search/common/descendant_indexing/section.yaml new file mode 100644 index 00000000..64c05099 --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/section.yaml @@ -0,0 +1,8 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.message_handler.assign_section: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Section\AssignSectionHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } diff --git a/lib/Resources/config/search/common/descendant_indexing/trash.yaml b/lib/Resources/config/search/common/descendant_indexing/trash.yaml new file mode 100644 index 00000000..2a4fdbf2 --- /dev/null +++ b/lib/Resources/config/search/common/descendant_indexing/trash.yaml @@ -0,0 +1,20 @@ +services: + netgen.ibexa_search_extra.descendant_indexing.message_handler.recover: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\RecoverHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } + + netgen.ibexa_search_extra.descendant_indexing.message_handler.trash: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\TrashHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + - '@?logger' + tags: + - { name: netgen.ibexa_search_extra.descendant_indexing.message_handler } + - { name: monolog.logger, channel: ngsearchextra } diff --git a/lib/Resources/config/search/solr/descendant_indexing/field_mappers.yaml b/lib/Resources/config/search/solr/descendant_indexing/field_mappers.yaml new file mode 100644 index 00000000..4c14a845 --- /dev/null +++ b/lib/Resources/config/search/solr/descendant_indexing/field_mappers.yaml @@ -0,0 +1,56 @@ +services: + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.block: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\Block\DescendantFieldMapper + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: ibexa.search.solr.field.mapper.block } + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.block.translation: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\BlockTranslation\DescendantFieldMapper + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: ibexa.search.solr.field.mapper.block.translation } + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\Content\DescendantFieldMapper + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: ibexa.search.solr.field.mapper.content } + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\ContentTranslation\DescendantFieldMapper + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: ibexa.search.solr.field.mapper.content.translation } + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.location: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\Location\DescendantFieldMapper + arguments: + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: ibexa.search.solr.field.mapper.location } + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation.fulltext_field_resolver.native: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\ContentTranslation\DescendantFieldMapper\FullTextFieldResolver\NativeFullTextFieldResolver + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + - '@Ibexa\Core\Search\Common\FieldRegistry' + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation.fulltext_field_resolver: + alias: netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation.fulltext_field_resolver.native + + netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation.fulltext: + class: Netgen\IbexaSearchExtra\Core\Search\Solr\FieldMapper\ContentTranslation\DescendantFieldMapper\FullTextFieldMapper + arguments: + - '@netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation.fulltext_field_resolver' + - '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Filter\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + tags: + - { name: netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content.translation } diff --git a/lib/Resources/config/search/solr_services.yaml b/lib/Resources/config/search/solr_services.yaml index 627744e0..e77a1b49 100644 --- a/lib/Resources/config/search/solr_services.yaml +++ b/lib/Resources/config/search/solr_services.yaml @@ -2,6 +2,7 @@ imports: - { resource: solr/criterion_visitors.yaml } - { resource: solr/facet_builder_visitors.yaml } - { resource: solr/field_mappers.yaml } + - { resource: solr/descendant_indexing/field_mappers.yaml } - { resource: solr/sort_clause_visitors.yaml } - { resource: solr/subdocument_mappers.yaml } diff --git a/phpunit-core-integration-legacy-solr-asynchronous.xml b/phpunit-core-integration-legacy-solr-asynchronous.xml new file mode 100644 index 00000000..387e6437 --- /dev/null +++ b/phpunit-core-integration-legacy-solr-asynchronous.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + tests/lib/Kernel + vendor/ibexa/core/tests/integration/Core + vendor/ibexa/core/tests/integration/Core/Repository/Filtering + + vendor/ibexa/core/tests/integration/Core/Repository/SearchServiceTest.php + vendor/ibexa/core/tests/integration/Core/Repository/SearchServiceLocationTest.php + + + + + tests/integration + + + diff --git a/phpunit-integration-legacy.xml b/phpunit-integration-legacy.xml index 69e3fc22..32e92a2c 100644 --- a/phpunit-integration-legacy.xml +++ b/phpunit-integration-legacy.xml @@ -19,8 +19,13 @@ tests/lib/Integration/API/SubdocumentQueryCriterionTest.php tests/lib/Integration/API/FulltextSpellcheckCriterionTest.php tests/lib/Integration/API/ExtraFieldsTest.php + tests/lib/Integration/API/DescendantIndexingTest.php tests/lib/Integration/Solr/RawFacetDomainTest.php tests/lib/Integration/Solr/RawFacetTest.php + tests/lib/Integration/Solr/DescendantIndexingContentTest.php + tests/lib/Integration/Solr/DescendantIndexingLocationTest.php + tests/lib/Integration/Solr/DescendantIndexingObjectStateTest.php + tests/lib/Integration/Solr/DescendantIndexingTrashTest.php diff --git a/phpunit-integration-solr-asynchronous.xml b/phpunit-integration-solr-asynchronous.xml new file mode 100644 index 00000000..2502fb9d --- /dev/null +++ b/phpunit-integration-solr-asynchronous.xml @@ -0,0 +1,23 @@ + + + + + + + + + + ./tests/lib/Integration/ + + + + + ./bundle + ./lib + + + diff --git a/phpunit-integration-solr.xml b/phpunit-integration-solr.xml index 65719810..37b56a26 100644 --- a/phpunit-integration-solr.xml +++ b/phpunit-integration-solr.xml @@ -12,6 +12,7 @@ ./tests/lib/Integration/ + tests/lib/Integration/API/DescendantIndexingTest.php diff --git a/tests/bundle/DependencyInjection/NetgenIbexaSearchExtraExtensionTest.php b/tests/bundle/DependencyInjection/NetgenIbexaSearchExtraExtensionTest.php index f1a23998..8a3742cb 100644 --- a/tests/bundle/DependencyInjection/NetgenIbexaSearchExtraExtensionTest.php +++ b/tests/bundle/DependencyInjection/NetgenIbexaSearchExtraExtensionTest.php @@ -6,6 +6,7 @@ use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; use Netgen\Bundle\IbexaSearchExtraBundle\DependencyInjection\NetgenIbexaSearchExtraExtension; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -91,6 +92,612 @@ public function testIndexableFieldTypeDefaultConfiguration(array $configuration) ); } + public function providerForTestHierarchicalIndexingConfiguration(): array + { + return [ + [ + [ + 'hierarchical_indexing' => false, + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => false, + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'mogoruš' => [], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => 'yes', + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => true, + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'mogoruš' => true, + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => 1, + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'mogoruš' => [], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => 2, + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'children' => 3, + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'mogoruš' => 3, + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'children' => [], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => false, + 'children' => [], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [], + 'children' => [], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + false, + ], + 'children' => [], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + ], + 'children' => [], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => 4, + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [], + ], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => [], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'mogoruš' => [], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => false, + ], + ], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => 'many', + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [], + ], + ], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => 7, + ], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => [], + ], + ], + ], + ], + ], + ], + ], + ], + true, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => 8, + ], + ], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'handlers' => [], + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [], + ], + ], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => [ + 'handlers' => [], + 'indexed' => true, + 'children' => [], + ], + ], + ], + ], + ], + ], + ], + ], + ], + false, + ], + [ + [ + 'hierarchical_indexing' => [ + 'descendant_indexing' => [ + 'enabled' => false, + 'map' => [ + 'content_type_identifier' => [ + 'handlers' => [ + 'handler_identifier_1', + 'handler_identifier_2', + ], + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [ + 'content_type_identifier' => [ + 'indexed' => true, + 'children' => [], + ], + ], + ], + ], + ], + ], + ], + ], + ], + true, + ], + ]; + } + + /** + * @dataProvider providerForTestHierarchicalIndexingConfiguration + */ + public function testHierarchicalIndexingInvalidConfiguration(array $configuration, bool $valid): void + { + if (!$valid) { + $this->expectException(InvalidConfigurationException::class); + } else { + $this->addToAssertionCount(1); + } + + $this->load($configuration); + } + protected function getContainerExtensions(): array { return [ diff --git a/tests/lib/Integration/API/DescendantIndexingContentTest.php b/tests/lib/Integration/API/DescendantIndexingContentTest.php new file mode 100644 index 00000000..61d25e48 --- /dev/null +++ b/tests/lib/Integration/API/DescendantIndexingContentTest.php @@ -0,0 +1,330 @@ +getRepository(); + $searchService = $repository->getSearchService(); + + $content = $this->createContentForTesting(); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($content['parentContentId']), + new Query\Criterion\FullText('šćenac'), + new Query\Criterion\FullText('more'), + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(1, $searchResult->totalCount); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals($parentContentFound->id, $content['parentContentId']); + } + + /** + * @return void + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @depends testPublishVersion + */ + public function testDeleteContent(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + + $content = $this->createContentForTesting(); + $grandChildContent = $contentService->loadContent($content['grandChildContentId']); + $parentContent = $contentService->loadContent($content['parentContentId']); + + $contentService->deleteContent($grandChildContent->contentInfo); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentContent->id), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + } + + /** + * @return void + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @depends testPublishVersion + */ + public function testDeleteTranslation(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + + $content = $this->createContentForTesting(); + $grandChildContent = $contentService->loadContent($content['grandChildContentId']); + $parentContent = $contentService->loadContent($content['parentContentId']); + + $contentService->deleteTranslation($grandChildContent->contentInfo, 'ger-DE'); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentContent->id), + new Query\Criterion\FullText('mogorush') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + } + + + /** + * @return int + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testHideContent(): int + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + + $content = $this->createContentForTesting(); + $grandChildContent = $contentService->loadContent($content['grandChildContentId']); + $parentContent = $contentService->loadContent($content['parentContentId']); + + $contentService->hideContent($grandChildContent->contentInfo); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentContent->id), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + + return $grandChildContent->id; + } + + /** + * @param int $hiddenContentId + * @return void + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @depends testHideContent + */ + public function testRevealContent(int $hiddenContentId): void + { + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + + $hiddenContent = $contentService->loadContent($hiddenContentId); + $parentLocation = $hiddenContent->contentInfo->getMainLocation()->getParentLocation()->getParentLocation(); + + $contentService->revealContent($hiddenContent->contentInfo); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentLocation->contentId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($parentContentFound->id, $parentLocation->contentId); + + } + + /** + * @return void + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateContentMetadataHandler(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + + $content = $this->createContentForTesting(); + $grandChildContent = $contentService->loadContent($content['grandChildContentId']); + $parentContent = $contentService->loadContent($content['parentContentId']); + + // Creates a metadata update struct + $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); + + $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222'; + $metadataUpdate->mainLanguageCode = 'eng-GB'; + $metadataUpdate->alwaysAvailable = false; + $metadataUpdate->publishedDate = $this->createDateTime(441759600); // 1984/01/01 + $metadataUpdate->modificationDate = $this->createDateTime(441759600); // 1984/01/01 + + // Update the metadata of the published content object + $grandChildContent = $contentService->updateContentMetadata( + $grandChildContent->contentInfo, + $metadataUpdate + ); + + + $this->assertInstanceOf( + Content::class, + $grandChildContent + ); + + } + + /** + * @param ContentTypeService $contentTypeService + * @param string $identifier + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentType(ContentTypeService $contentTypeService, string $identifier): \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + { + + $contentTypeGroups = $contentTypeService->loadContentTypeGroups(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Descendant indexing test']; + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinitionCreateStruct->position = 0; + $fieldDefinitionCreateStruct->isSearchable = true; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + $contentTypeDraft = $contentTypeService->createContentType($contentTypeCreateStruct, [reset($contentTypeGroups)]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + + } + + /** + * @return array + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentForTesting(): array + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $this->createContentType($contentTypeService, 'descendant_indexing_test'); + $contentType2 = $this->createContentType($contentTypeService, 'descendant_indexing_test_2'); + $contentType3 = $this->createContentType($contentTypeService, 'descendant_indexing_test_3'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'mogoruš'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $parentContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $parentLocation = $parentContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType2, 'eng-GB'); + $contentCreateStruct->setField('name', 'šćenac'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $childContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $childLocation = $childContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($childLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType3, 'eng-GB'); + $contentCreateStruct->setField('name', 'more'); + //for testDeleteTranslation() + $contentCreateStruct->setField('name', 'mogorush', 'ger-DE'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $grandChildContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + + return [ + 'parentContentId' => $parentContentCreated->id, + 'childContentId' => $childContentCreated->id, + 'grandChildContentId' => $grandChildContentCreated->id + ]; + } + +} diff --git a/tests/lib/Integration/API/DescendantIndexingLocationTest.php b/tests/lib/Integration/API/DescendantIndexingLocationTest.php new file mode 100644 index 00000000..ab88fa63 --- /dev/null +++ b/tests/lib/Integration/API/DescendantIndexingLocationTest.php @@ -0,0 +1,539 @@ +getRepository(); + $searchService = $repository->getSearchService(); + $sectionService = $repository->getSectionService(); + + $mediaSectionId = $this->generateId('section', 3); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + // Load a location instance + $locations = $this->createLocationsForTesting(); + $childLocationId = $locations['childLocationId']; + $parentLocationId = $locations['parentLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + + $parentLocation = $locationService->loadLocation($parentLocationId); + // Load the "Media" section + $section = $sectionService->loadSection($mediaSectionId); + + // Assign Section to ContentInfo + $sectionService->assignSectionToSubtree($parentLocation, $section); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\CustomField( + 'ng_child_section_i', + Operator::EQ, + $mediaSectionId), + new Query\Criterion\SectionId($mediaSectionId), + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(1, $searchResult->totalCount); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + self::assertEquals($parentLocation->contentId, $parentContentFound->id); + + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testCopySubtree(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + + $locations = $this->createLocationsForTesting(); + $childLocationId = $locations['childLocationId']; + $siblingLocationId = $locations['siblingLocationId']; + + // Load location to copy + $locationToCopy = $locationService->loadLocation($childLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($siblingLocationId); + + $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + + $siblingLocation = $locationService->loadLocation($siblingLocationId); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($siblingLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($siblingLocation->contentId, $parentContentFound->id); + + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testCreateLocation(): void + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $locationService = $repository->getLocationService(); + + $locations = $this->createLocationsForTesting(); + $parentLocationId = $locations['parentLocationId']; + + $parentLocation = $locationService->loadLocation($parentLocationId); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($parentContentFound->id, $parentLocation->contentId); + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testDeleteLocation(): void + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $locationService = $repository->getLocationService(); + + $locations = $this->createLocationsForTesting(); + $parentLocationId = $locations['parentLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + $grandChildLocation = $locationService->loadLocation($grandChildLocationId); + + $locationService->deleteLocation($grandChildLocation); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testMoveSubtree(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + + $locations = $this->createLocationsForTesting(); + $parentLocationId = $locations['parentLocationId']; + $childLocationId = $locations['childLocationId']; + $siblingLocationId = $locations['siblingLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + + // Load location to copy + $locationToMove = $locationService->loadLocation($childLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($siblingLocationId); + + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + $siblingLocation = $locationService->loadLocation($siblingLocationId); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($siblingLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($siblingLocation->contentId, $parentContentFound->id); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testSwapLocation(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + + $locations = $this->createLocationsForTesting(); + + $siblingLocationId = $locations['siblingLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + $parentLocationId = $locations['parentLocationId']; + + $siblingLocation = $locationService->loadLocation($siblingLocationId); + $grandChildLocation = $locationService->loadLocation($grandChildLocationId); + + $locationService->swapLocation( + $siblingLocation, + $grandChildLocation + ); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('sibling') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + } + + /** + * @return array + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testHideLocation(): array + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $locationService = $repository->getLocationService(); + + $locations = $this->createLocationsForTesting(); + $parentLocationId = $locations['parentLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + $grandChildLocation = $locationService->loadLocation($grandChildLocationId); + + $locationService->hideLocation($grandChildLocation); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + + return [ + 'parentLocationId' => $parentLocationId, + 'grandChildLocationId' => $grandChildLocationId + ]; + } + + /** + * @param array $locations + * @return void + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @depends testHideLocation + */ + public function testUnhideLocation(array $locations): void + { + $repository = $this->getRepository(false); + $searchService = $repository->getSearchService(); + $locationService = $repository->getLocationService(); + + $parentLocationId = $locations['parentLocationId']; + $grandChildLocationId = $locations['grandChildLocationId']; + $grandChildLocation = $locationService->loadLocation($grandChildLocationId); + + $parentLocation = $locationService->loadLocation($parentLocationId); + + $locationService->unhideLocation($grandChildLocation); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($parentLocation->contentId, $parentContentFound->id); + } + + /** + * @return void + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testUpdateLocation(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + + $locations = $this->createLocationsForTesting(); + $grandChildLocationId = $locations['grandChildLocationId']; + $parentLocationId = $locations['parentLocationId']; + + $grandChildLocation = $locationService->loadLocation($grandChildLocationId); + $parentLocation = $locationService->loadLocation($parentLocationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + $updateStruct->priority = 3; + + $updatedLocation = $locationService->updateLocation($grandChildLocation, $updateStruct); + + $this->refreshSearch($repository); + + $query = new LocationQuery([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\CustomField( + 'ng_grandchild_location_priority_i', + Operator::EQ, + 3) + ]), + ]); + + $searchResult = $searchService->findLocations($query); + self::assertEquals(1, $searchResult->totalCount); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals($parentLocation->contentId, $parentContentFound->contentInfo->id); + } + + /** + * @param ContentTypeService $contentTypeService + * @param string $identifier + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * @throws BadStateException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createContentType(ContentTypeService $contentTypeService, string $identifier): \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + { + $contentTypeGroups = $contentTypeService->loadContentTypeGroups(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Descendant indexing test']; + $contentTypeCreateStruct->isContainer = true; + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinitionCreateStruct->position = 0; + $fieldDefinitionCreateStruct->isSearchable = true; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + $contentTypeDraft = $contentTypeService->createContentType($contentTypeCreateStruct, [reset($contentTypeGroups)]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + + } + + /** + * @return array + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createLocationsForTesting(): array + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $this->createContentType($contentTypeService, 'descendant_indexing_test'); + $contentType2 = $this->createContentType($contentTypeService, 'descendant_indexing_test_2'); + $contentType3 = $this->createContentType($contentTypeService, 'descendant_indexing_test_3'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'mogoruš'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $parentContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $parentLocation = $parentContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'sibling'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $siblingLocationCreated = $contentService->publishVersion($contentDraft->versionInfo); + $siblingLocation = $siblingLocationCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType2, 'eng-GB'); + $contentCreateStruct->setField('name', 'šćenac'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $childContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $childLocation = $childContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($childLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType3, 'eng-GB'); + $contentCreateStruct->setField('name', 'more'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $grandChildContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $grandChildLocation = $grandChildContentCreated->contentInfo->getMainLocation(); + + return [ + 'parentLocationId' => $parentLocation->id, + 'siblingLocationId' => $siblingLocation->id, + 'childLocationId' => $childLocation->id, + 'grandChildLocationId' => $grandChildLocation->id + ]; + } +} diff --git a/tests/lib/Integration/API/DescendantIndexingObjectStateTest.php b/tests/lib/Integration/API/DescendantIndexingObjectStateTest.php new file mode 100644 index 00000000..e4d1fe54 --- /dev/null +++ b/tests/lib/Integration/API/DescendantIndexingObjectStateTest.php @@ -0,0 +1,138 @@ +getRepository(); + $contentService = $this->getRepository()->getContentService(); + $searchService = $this->getRepository()->getSearchService(); + $objectStateService = $this->getRepository()->getObjectStateService(); + + $content = $this->createContentForTesting(); + $grandChildContentId = $content['grandChildContentId']; + $parentContentId = $content['parentContentId']; + $grandChildContent = $contentService->loadContent($grandChildContentId); + $parentContent = $contentService->loadContent($parentContentId); + + $stateGroup = $objectStateService->loadObjectStateGroupByIdentifier( + "ez_lock", + ); + + $lockedState = $objectStateService->loadObjectStateByIdentifier($stateGroup, 'locked'); + + $objectStateService->setContentState($grandChildContent->contentInfo, $stateGroup, $lockedState); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentContent->contentInfo->mainLocationId), + new Query\Criterion\CustomField( + 'ng_grandchild_object_state_s', + Operator::EQ, + $lockedState->identifier + ) + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(1, $searchResult->totalCount); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + self::assertEquals($parentContentId, $parentContentFound->id); + } + + + /** + * @return array + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentForTesting(): array + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $this->createContentType($contentTypeService, 'descendant_indexing_test'); + $contentType2 = $this->createContentType($contentTypeService, 'descendant_indexing_test_2'); + $contentType3 = $this->createContentType($contentTypeService, 'descendant_indexing_test_3'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'mogoruš'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $parentContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $parentLocation = $parentContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType2, 'eng-GB'); + $contentCreateStruct->setField('name', 'šćenac'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $childContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $childLocation = $childContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($childLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType3, 'eng-GB'); + $contentCreateStruct->setField('name', 'more'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $grandChildContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + + return [ + 'parentContentId' => $parentContentCreated->id, + 'childContentId' => $childContentCreated->id, + 'grandChildContentId' => $grandChildContentCreated->id + ]; + } + + /** + * @param ContentTypeService $contentTypeService + * @param string $identifier + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentType(ContentTypeService $contentTypeService, string $identifier): \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + { + $contentTypeGroups = $contentTypeService->loadContentTypeGroups(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Descendant indexing test']; + $contentTypeCreateStruct->isContainer = true; + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinitionCreateStruct->position = 0; + $fieldDefinitionCreateStruct->isSearchable = true; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + $contentTypeDraft = $contentTypeService->createContentType($contentTypeCreateStruct, [reset($contentTypeGroups)]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + + } +} diff --git a/tests/lib/Integration/API/DescendantIndexingSectionTest.php b/tests/lib/Integration/API/DescendantIndexingSectionTest.php new file mode 100644 index 00000000..b6ca334d --- /dev/null +++ b/tests/lib/Integration/API/DescendantIndexingSectionTest.php @@ -0,0 +1,154 @@ +getRepository(); + $searchService = $repository->getSearchService(); + $sectionService = $repository->getSectionService(); + $locationService = $repository->getLocationService(); + + $mediaSectionId = $this->generateId('section', 3); + + $locations = $this->createLocationsForTesting(); + $childLocationId = $locations['childLocationId']; + $parentLocationId = $locations['parentLocationId']; + + $parentLocation = $locationService->loadLocation($parentLocationId); + $childLocation = $locationService->loadLocation($childLocationId); + + $section = $sectionService->loadSection($mediaSectionId); + + $sectionService->assignSection($childLocation->contentInfo, $section); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\LocationId($parentLocationId), + new Query\Criterion\CustomField( + 'ng_child_section_i', + Operator::EQ, + $mediaSectionId) + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(1, $searchResult->totalCount); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + self::assertEquals($parentLocation->contentId, $parentContentFound->id); + } + + + /** + * @return array + * @throws BadStateException + * @throws ContentFieldValidationException + * @throws ContentValidationException + * @throws InvalidArgumentException + * @throws UnauthorizedException + */ + private function createLocationsForTesting(): array + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $this->createContentType($contentTypeService, 'descendant_indexing_test'); + $contentType2 = $this->createContentType($contentTypeService, 'descendant_indexing_test_2'); + $contentType3 = $this->createContentType($contentTypeService, 'descendant_indexing_test_3'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'mogoruš'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $parentContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $parentLocation = $parentContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'sibling'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $siblingLocationCreated = $contentService->publishVersion($contentDraft->versionInfo); + $siblingLocation = $siblingLocationCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType2, 'eng-GB'); + $contentCreateStruct->setField('name', 'šćenac'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $childContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $childLocation = $childContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($childLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType3, 'eng-GB'); + $contentCreateStruct->setField('name', 'more'); + $contentCreateStruct->setField('name', 'mogorush', 'ger-DE'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $grandChildContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $grandChildLocation = $grandChildContentCreated->contentInfo->getMainLocation(); + + return [ + 'parentLocationId' => $parentLocation->id, + 'siblingLocationId' => $siblingLocation->id, + 'childLocationId' => $childLocation->id, + 'grandChildLocationId' => $grandChildLocation->id + ]; + } + + /** + * @param ContentTypeService $contentTypeService + * @param string $identifier + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * @throws BadStateException + * @throws InvalidArgumentException + * @throws UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createContentType(ContentTypeService $contentTypeService, string $identifier): \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + { + $contentTypeGroups = $contentTypeService->loadContentTypeGroups(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Descendant indexing test']; + $contentTypeCreateStruct->isContainer = true; + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinitionCreateStruct->position = 0; + $fieldDefinitionCreateStruct->isSearchable = true; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + $contentTypeDraft = $contentTypeService->createContentType($contentTypeCreateStruct, [reset($contentTypeGroups)]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + + } +} diff --git a/tests/lib/Integration/API/DescendantIndexingTrashTest.php b/tests/lib/Integration/API/DescendantIndexingTrashTest.php new file mode 100644 index 00000000..c7e458c3 --- /dev/null +++ b/tests/lib/Integration/API/DescendantIndexingTrashTest.php @@ -0,0 +1,153 @@ +getRepository(); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + $trashService = $repository->getTrashService(); + + $content = $this->createContentForTesting(); + $grandChildContent = $contentService->loadContent($content['grandChildContentId']); + $parentContent = $contentService->loadContent($content['parentContentId']); + + $trashItem = $trashService->trash($grandChildContent->contentInfo->getMainLocation()); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentContent->id), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(0, $searchResult->totalCount); + + return [ + "trashItem" => $trashItem, + "content" => $content + ]; + } + + /** + * @param array $sharedObjects + * @return void + * @throws InvalidArgumentException + * @throws InvalidCriterionArgumentException + * @throws NotFoundException + * @throws UnauthorizedException + * @depends testTrash + */ + public function testRecover(array $sharedObjects): void + { + $trashItem = $sharedObjects['trashItem']; + $content = $sharedObjects['content']; + + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + $searchService = $repository->getSearchService(); + $trashService = $repository->getTrashService(); + + $parentContent = $contentService->loadContent($content['parentContentId']); + + $location = $trashService->recover($trashItem); + + $this->refreshSearch($repository); + + $query = new Query([ + 'filter' => new Query\Criterion\LogicalAnd([ + new Query\Criterion\ContentId($parentContent->id), + new Query\Criterion\FullText('more') + ]), + ]); + + $searchResult = $searchService->findContent($query); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $parentContentFound */ + $parentContentFound = $searchResult->searchHits[0]->valueObject; + + self::assertEquals(1, $searchResult->totalCount); + self::assertEquals($parentContentFound->id, $parentContent->id); + } + + + + private function createContentType(ContentTypeService $contentTypeService, string $identifier) { + + $contentTypeGroups = $contentTypeService->loadContentTypeGroups(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Descendant indexing test']; + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinitionCreateStruct->position = 0; + $fieldDefinitionCreateStruct->isSearchable = true; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + $contentTypeDraft = $contentTypeService->createContentType($contentTypeCreateStruct, [reset($contentTypeGroups)]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + + } + + private function createContentForTesting() { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $this->createContentType($contentTypeService, 'descendant_indexing_test'); + $contentType2 = $this->createContentType($contentTypeService, 'descendant_indexing_test_2'); + $contentType3 = $this->createContentType($contentTypeService, 'descendant_indexing_test_3'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', 'mogoruš'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $parentContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $parentLocation = $parentContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($parentLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType2, 'eng-GB'); + $contentCreateStruct->setField('name', 'šćenac'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $childContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + $childLocation = $childContentCreated->contentInfo->getMainLocation(); + + $locationCreateStruct = $locationService->newLocationCreateStruct($childLocation->id); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType3, 'eng-GB'); + $contentCreateStruct->setField('name', 'more'); + $contentDraft = $contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + $grandChildContentCreated = $contentService->publishVersion($contentDraft->versionInfo); + + return [ + 'parentContentId' => $parentContentCreated->id, + 'childContentId' => $childContentCreated->id, + 'grandChildContentId' => $grandChildContentCreated->id + ]; + } + +} diff --git a/tests/lib/Integration/Implementation/Solr/FieldMapper/TestLocationPriorityFieldMapper.php b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestLocationPriorityFieldMapper.php new file mode 100644 index 00000000..fb69b442 --- /dev/null +++ b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestLocationPriorityFieldMapper.php @@ -0,0 +1,151 @@ + + */ + private array $contentTypeIdIdentifierCache; + + /** + * @param array $configuration + */ + public function __construct( + private readonly ContentTypeHandler $contentTypeHandler, + private readonly ContentHandler $contentHandler, + private readonly Handler $contentFilteringHandler, + private readonly LocationHandler $locationHandler, + private readonly array $configuration, + private readonly int $childrenLimit = 99, + ) { + parent::__construct( + $contentHandler, + $contentTypeHandler, + $this->configuration, + ); + } + + public function doAccept(SPILocation $location): bool + { + return true; + } + + /** + * @param string $languageCode + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function mapFields(SPILocation $location): array + { + $content = $this->contentHandler->load($location->contentId); + $contentInfo = $content->versionInfo->contentInfo; + $contentTypeId = $content->versionInfo->contentInfo->contentTypeId; + $contentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + $childrenConfiguration = $this->configuration['map'][$contentTypeIdentifier]['children']; + $contentTypeIdentifier = array_keys($childrenConfiguration)[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($contentTypeIdentifier), + new ParentLocationId($contentInfo->mainLocationId), + ]) + ); + + $contentItemList = $this->contentFilteringHandler->find($filter); + $fieldsGrouped = [[]]; + + foreach ($contentItemList as $contentItem) { + $contentTypeId = $contentItem->contentInfo->contentTypeId; + $childContentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + $childConfiguration = $childrenConfiguration[$childContentTypeIdentifier] ?? []; + $childLocation = $this->locationHandler->load($contentItem->contentInfo->mainLocationId); + + if (isset($childConfiguration['indexed']) && $childConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_child_location_priority', + $childLocation->priority, + new IntegerField(), + ), + ]; + } + + $grandChildContentTypeIdentifier = array_keys($childConfiguration['children'])[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($grandChildContentTypeIdentifier), + new ParentLocationId($contentItem->contentInfo->mainLocationId), + ]) + ) + ->withLimit($this->childrenLimit); + + $grandChildContentItemList = $this->contentFilteringHandler->find($filter); + foreach ($grandChildContentItemList as $grandChildContentItem) { + $grandChildConfiguration = $childConfiguration['children'][$grandChildContentTypeIdentifier] ?? []; + $grandChildLocation = $this->locationHandler->load($grandChildContentItem->contentInfo->mainLocationId); + if (isset($grandChildConfiguration['indexed']) && $grandChildConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_grandchild_location_priority', + $grandChildLocation->priority, + new IntegerField(), + ), + ]; + + } + + } + + } + + return array_merge(...$fieldsGrouped); + } + + public function getIdentifier(): string + { + return 'ng_descendant_indexing_location_priority'; + } + + private function getContentTypeIdentifier(int $contentTypeId): ?string + { + if (isset($this->contentTypeIdIdentifierCache[$contentTypeId])) { + return $this->contentTypeIdIdentifierCache[$contentTypeId]; + } + + try { + $contentType = $this->contentTypeHandler->load($contentTypeId); + $identifier = $contentType->identifier; + } catch (NotFoundException) { + $identifier = null; + } + + $this->contentTypeIdIdentifierCache[$contentTypeId] = $identifier; + + return $identifier; + } + +} diff --git a/tests/lib/Integration/Implementation/Solr/FieldMapper/TestObjectStateFieldMapper.php b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestObjectStateFieldMapper.php new file mode 100644 index 00000000..a52d1538 --- /dev/null +++ b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestObjectStateFieldMapper.php @@ -0,0 +1,160 @@ + + */ + private array $contentTypeIdIdentifierCache; + + /** + * @param array $configuration + */ + public function __construct( + private readonly ContentTypeHandler $contentTypeHandler, + private readonly Handler $contentFilteringHandler, + private readonly ObjectStateHandler $objectStateHandler, + private readonly array $configuration, + private readonly int $childrenLimit = 99, + ) { + parent::__construct( + $contentTypeHandler, + $this->configuration, + ); + } + + public function doAccept(Content $content): bool + { + return true; + } + + /** + * @param string $languageCode + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function mapFields(Content $content): array + { + $contentInfo = $content->versionInfo->contentInfo; + $contentTypeId = $content->versionInfo->contentInfo->contentTypeId; + $contentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + $childrenConfiguration = $this->configuration['map'][$contentTypeIdentifier]['children']; + $contentTypeIdentifier = array_keys($childrenConfiguration)[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($contentTypeIdentifier), + new ParentLocationId($contentInfo->mainLocationId), + ]) + ); + + $contentItemList = $this->contentFilteringHandler->find($filter); + $fieldsGrouped = [[]]; + + foreach ($contentItemList as $contentItem) { + $contentTypeId = $contentItem->contentInfo->contentTypeId; + $childContentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + $childConfiguration = $childrenConfiguration[$childContentTypeIdentifier] ?? []; + $stateGroup = $this->objectStateHandler->loadGroupByIdentifier( + "ez_lock", + ); + $objectState = $this->objectStateHandler->getContentState( + $contentItem->contentInfo->id, + $stateGroup->id, + ); + if (isset($childConfiguration['indexed']) && $childConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_child_object_state', + $objectState->identifier, + new StringField(), + ), + ]; + } + + $grandChildContentTypeIdentifier = array_keys($childConfiguration['children'])[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($grandChildContentTypeIdentifier), + new ParentLocationId($contentItem->contentInfo->mainLocationId), + ]) + ) + ->withLimit($this->childrenLimit); + + $grandChildContentItemList = $this->contentFilteringHandler->find($filter); + foreach ($grandChildContentItemList as $grandChildContentItem) { + + $stateGroup = $this->objectStateHandler->loadGroupByIdentifier( + "ez_lock", + ); + $objectState = $this->objectStateHandler->getContentState( + $grandChildContentItem->contentInfo->id, + $stateGroup->id, + ); + + $grandChildConfiguration = $childConfiguration['children'][$grandChildContentTypeIdentifier] ?? []; + if (isset($grandChildConfiguration['indexed']) && $grandChildConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_grandchild_object_state', + $objectState->identifier, + new StringField(), + ), + ]; + + } + + } + + } + + return array_merge(...$fieldsGrouped); + } + + public function getIdentifier(): string + { + return 'ng_descendant_indexing_object_state'; + } + + private function getContentTypeIdentifier(int $contentTypeId): ?string + { + if (isset($this->contentTypeIdIdentifierCache[$contentTypeId])) { + return $this->contentTypeIdIdentifierCache[$contentTypeId]; + } + + try { + $contentType = $this->contentTypeHandler->load($contentTypeId); + $identifier = $contentType->identifier; + } catch (NotFoundException) { + $identifier = null; + } + + $this->contentTypeIdIdentifierCache[$contentTypeId] = $identifier; + + return $identifier; + } +} diff --git a/tests/lib/Integration/Implementation/Solr/FieldMapper/TestSectionFieldMapper.php b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestSectionFieldMapper.php new file mode 100644 index 00000000..07e78624 --- /dev/null +++ b/tests/lib/Integration/Implementation/Solr/FieldMapper/TestSectionFieldMapper.php @@ -0,0 +1,143 @@ + + */ + private array $contentTypeIdIdentifierCache; + + /** + * @param array $configuration + */ + public function __construct( + private readonly ContentTypeHandler $contentTypeHandler, + private readonly Handler $contentFilteringHandler, + private readonly array $configuration, + private readonly int $childrenLimit = 99, + ) { + parent::__construct( + $contentTypeHandler, + $this->configuration, + ); + } + + public function doAccept(Content $content): bool + { + return true; + } + + /** + * @param string $languageCode + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function mapFields(Content $content): array + { + $contentInfo = $content->versionInfo->contentInfo; + $contentTypeId = $content->versionInfo->contentInfo->contentTypeId; + $contentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + $childrenConfiguration = $this->configuration['map'][$contentTypeIdentifier]['children']; + $contentTypeIdentifier = array_keys($childrenConfiguration)[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($contentTypeIdentifier), + new ParentLocationId($contentInfo->mainLocationId), + ]) + ); + + $contentItemList = $this->contentFilteringHandler->find($filter); + $fieldsGrouped = [[]]; + + foreach ($contentItemList as $contentItem) { + $contentTypeId = $contentItem->contentInfo->contentTypeId; + $childContentTypeIdentifier = $this->getContentTypeIdentifier($contentTypeId); + + $childConfiguration = $childrenConfiguration[$childContentTypeIdentifier] ?? []; + + if (isset($childConfiguration['indexed']) && $childConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_child_section', + $contentItem->contentInfo->sectionId, + new IntegerField(), + ), + ]; + } + + $grandChildContentTypeIdentifier = array_keys($childConfiguration['children'])[0]; + $filter = new Filter(); + $filter + ->withCriterion( + new LogicalAnd([ + new ContentTypeIdentifier($grandChildContentTypeIdentifier), + new ParentLocationId($contentItem->contentInfo->mainLocationId), + ]) + ) + ->withLimit($this->childrenLimit); + + $grandChildContentItemList = $this->contentFilteringHandler->find($filter); + foreach ($grandChildContentItemList as $grandChildContentItem) { + $grandChildConfiguration = $childConfiguration['children'][$grandChildContentTypeIdentifier] ?? []; + if (isset($grandChildConfiguration['indexed']) && $grandChildConfiguration['indexed'] === true) { + $fieldsGrouped[] = [ + new Field( + 'ng_grandchild_section', + $grandChildContentItem->contentInfo->sectionId, + new IntegerField(), + ), + ]; + + } + + } + + } + + return array_merge(...$fieldsGrouped); + } + + public function getIdentifier(): string + { + return 'ng_descendant_indexing_section'; + } + + private function getContentTypeIdentifier(int $contentTypeId): ?string + { + if (isset($this->contentTypeIdIdentifierCache[$contentTypeId])) { + return $this->contentTypeIdIdentifierCache[$contentTypeId]; + } + + try { + $contentType = $this->contentTypeHandler->load($contentTypeId); + $identifier = $contentType->identifier; + } catch (NotFoundException) { + $identifier = null; + } + + $this->contentTypeIdIdentifierCache[$contentTypeId] = $identifier; + + return $identifier; + } +} diff --git a/tests/lib/Integration/Resources/config/event_dispatcher_override_asynchronous.yaml b/tests/lib/Integration/Resources/config/event_dispatcher_override_asynchronous.yaml new file mode 100644 index 00000000..b5514c18 --- /dev/null +++ b/tests/lib/Integration/Resources/config/event_dispatcher_override_asynchronous.yaml @@ -0,0 +1,316 @@ +parameters: + netgen.ibexa_search_extra.descendant_indexing.configuration: + enabled: true + map: + descendant_indexing_test: + handlers: + - ng_descendant_indexing_fulltext + - ng_descendant_indexing_location_priority + - ng_descendant_indexing_section + - ng_descendant_indexing_object_state + children: + descendant_indexing_test_2: + indexed: true + children: + descendant_indexing_test_3: + indexed: true + +services: + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\CopyContentHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\CopyContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteContentHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteTranslationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteTranslationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\HideContentHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\HideContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\PublishVersionHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\PublishVersionHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\RevealContentHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\RevealContentHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\UpdateContentMetadataHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\UpdateContentMetadataHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\AssignSectionToSubtreeHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\AssignSectionToSubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CopySubtreeHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CopySubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CreateLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CreateLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\DeleteLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\DeleteLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\HideLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\HideLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\MoveSubtreeHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\MoveSubtreeHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\SwapLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\SwapLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UnhideLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UnhideLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UpdateLocationHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UpdateLocationHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\ObjectState\SetContentStateHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\ObjectState\SetContentStateHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Section\AssignSectionHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Section\AssignSectionHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\RecoverHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\RecoverHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\TrashHandler: + class: Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\TrashHandler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + - '@netgen.ibexa_search_extra.descendant_indexing.ancestor_indexer' + tags: + - { name: messenger.message_handler } + + netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.handler.locator: + class: Symfony\Component\Messenger\Handler\HandlersLocator + arguments: + - + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\CopyContent': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\CopyContentHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\CopyContentHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\DeleteContentHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteContentHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteTranslation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\DeleteTranslationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\DeleteTranslationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\HideContent': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\HideContentHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\HideContentHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\PublishVersion': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\PublishVersionHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\PublishVersionHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\RevealContent': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\RevealContentHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\RevealContentHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\UpdateContentMetadata': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Content\UpdateContentMetadataHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Content\UpdateContentMetadataHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\AssignSectionToSubtree': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\AssignSectionToSubtreeHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\AssignSectionToSubtreeHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\CopySubtree': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\CopySubtreeHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CopySubtreeHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\CreateLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\CreateLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\CreateLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\DeleteLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\DeleteLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\DeleteLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\HideLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\HideLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\HideLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\MoveSubtree': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\MoveSubtreeHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\MoveSubtreeHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\SwapLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\SwapLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\SwapLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\UnhideLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\UnhideLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UnhideLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\UpdateLocation': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Location\UpdateLocationHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Location\UpdateLocationHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\ObjectState\SetContentState': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\ObjectState\SetContentStateHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\ObjectState\SetContentStateHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Section\AssignSection': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Section\AssignSectionHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Section\AssignSectionHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Recover': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash\RecoverHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\RecoverHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Trash': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\Trash\TrashHandler' + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\DescendantIndexing\Trash\TrashHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\AssignUserToUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\AssignUserToUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\BeforeUnAssignUserFromUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\BeforeUnAssignUserFromUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\CreateUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\CreateUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\CreateUser': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\CreateUserHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\DeleteUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUser': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\DeleteUserHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\MoveUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\MoveUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UnAssignUserFromUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UnAssignUserFromUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UpdateUserGroup': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UpdateUserGroupHandler' + 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UpdateUser': + - '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\AsynchronousIndexing\User\UpdateUserHandler' + + netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.middleware: + class: Symfony\Component\Messenger\Middleware\HandleMessageMiddleware + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.handler.locator' + + netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus: + class: Symfony\Component\Messenger\MessageBus + arguments: + - ['@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.middleware'] + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.content: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\ContentEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + - '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.location: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\LocationEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.object_state: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\ObjectStateEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.section: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\SectionEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.trash: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\TrashEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.user: + class: Netgen\IbexaSearchExtra\Core\Search\Common\EventSubscriber\UserEventSubscriber + arguments: + - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + Symfony\Component\EventDispatcher\EventDispatcher: + calls: + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.solr.event_subscriber.child_updates_parent']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.content']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.location']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.object_state']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.section']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.trash']] + - ['addSubscriber', ['@netgen_test.ibexa_search_extra.asynchronous_indexing.event_subscriber.user']] + - ['addSubscriber', ['@Ibexa\Core\Repository\EventSubscriber\NameSchemaSubscriber' ]] diff --git a/tests/lib/Integration/Resources/config/services.yaml b/tests/lib/Integration/Resources/config/services.yaml index 0998051e..874e895e 100644 --- a/tests/lib/Integration/Resources/config/services.yaml +++ b/tests/lib/Integration/Resources/config/services.yaml @@ -40,49 +40,40 @@ services: tags: - { name: ibexa.search.solr.field.mapper.location } - netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus: - class: Symfony\Component\Messenger\MessageBus + netgen.ibexa_search_extra.solr.field_mapper.location.location_priority: + class: Netgen\IbexaSearchExtra\Tests\Integration\Implementation\Solr\FieldMapper\TestLocationPriorityFieldMapper arguments: - - ['@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.middleware'] + [ + '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler', + '@Ibexa\Contracts\Core\Persistence\Content\Handler', + '@Ibexa\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler', + '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler', + '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + ] + tags: + - { name: netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.location } - netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.middleware: - class: Symfony\Component\Messenger\Middleware\HandleMessageMiddleware - arguments: - - '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.handler.locator' - netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.handler.locator: - class: Symfony\Component\Messenger\Handler\HandlersLocator + netgen.ibexa_search_extra.solr.field_mapper.content.section: + class: Netgen\IbexaSearchExtra\Tests\Integration\Implementation\Solr\FieldMapper\TestSectionFieldMapper arguments: - - - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\CopyContent': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\CopyContentHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteContent': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\DeleteContentHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\DeleteTranslation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\DeleteTranslationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\HideContent': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\HideContentHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\PublishVersion': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\PublishVersionHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\RevealContent': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\RevealContentHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Content\UpdateContentMetadata': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Content\UpdateContentMetadataHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\AssignSectionToSubtree': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\AssignSectionToSubtreeHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\CopySubtree': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\CopySubtreeHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\CreateLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\CreateLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\DeleteLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\DeleteLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\HideLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\HideLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\MoveSubtree': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\MoveSubtreeHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\SwapLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\SwapLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\UnhideLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\UnhideLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Location\UpdateLocation': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Location\UpdateLocationHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\ObjectState\SetContentState': [ '@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\ObjectState\SetContentStateHandler' ] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Section\AssignSection': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Section\AssignSectionHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Recover': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash\RecoverHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\Trash\Trash': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\Trash\TrashHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\AssignUserToUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\AssignUserToUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\BeforeUnAssignUserFromUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\BeforeUnAssignUserFromUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\CreateUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\CreateUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\CreateUser': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\CreateUserHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\DeleteUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\DeleteUser': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\DeleteUserHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\MoveUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\MoveUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UnAssignUserFromUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UnAssignUserFromUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UpdateUserGroup': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UpdateUserGroupHandler'] - 'Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\Message\Search\User\UpdateUser': ['@Netgen\IbexaSearchExtra\Core\Search\Common\Messenger\MessageHandler\Search\User\UpdateUserHandler'] + [ + '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler', + '@Ibexa\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler', + '%netgen.ibexa_search_extra.descendant_indexing.configuration%', + ] + tags: + - { name: netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content } - netgen.ibexa_search_extra.asynchronous_indexing.messenger.bus: '@netgen_test.ibexa_search_extra.asynchronous_indexing.messenger.bus' + + netgen.ibexa_search_extra.solr.field_mapper.content.object_state: + class: Netgen\IbexaSearchExtra\Tests\Integration\Implementation\Solr\FieldMapper\TestObjectStateFieldMapper + arguments: + [ + '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler', + '@Ibexa\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler', + '@Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler', + '%netgen.ibexa_search_extra.descendant_indexing.configuration%' + ] + tags: + - { name: netgen.ibexa_search_extra.solr.field_mapper.descendant_indexing.content } diff --git a/tests/lib/Integration/SetupFactory/RegressionSolr.php b/tests/lib/Integration/SetupFactory/RegressionSolr.php index 132fde20..5ee28e6e 100644 --- a/tests/lib/Integration/SetupFactory/RegressionSolr.php +++ b/tests/lib/Integration/SetupFactory/RegressionSolr.php @@ -30,6 +30,7 @@ protected function externalBuildContainer(ContainerBuilder $containerBuilder): v $testConfigPath = __DIR__ . '/../Resources/config/'; $loader = new YamlFileLoader($containerBuilder, new FileLocator($testConfigPath)); $loader->load('services.yaml'); + $loader->load('event_dispatcher_override.yaml'); // Needs to be added first because other passes depend on it $containerBuilder->addCompilerPass(new Compiler\TagSubdocumentCriterionVisitorsPass()); diff --git a/tests/lib/Integration/SetupFactory/RegressionSolrAsynchronous.php b/tests/lib/Integration/SetupFactory/RegressionSolrAsynchronous.php new file mode 100644 index 00000000..03107fa1 --- /dev/null +++ b/tests/lib/Integration/SetupFactory/RegressionSolrAsynchronous.php @@ -0,0 +1,43 @@ +load('search/common.yaml'); + $loader->load('search/solr_services.yaml'); + $loader->load('search/solr_engine.yaml'); + + $testConfigPath = __DIR__ . '/../Resources/config/'; + $loader = new YamlFileLoader($containerBuilder, new FileLocator($testConfigPath)); + $loader->load('services.yaml'); + $loader->load('event_dispatcher_override_asynchronous.yaml'); + + // Needs to be added first because other passes depend on it + $containerBuilder->addCompilerPass(new Compiler\TagSubdocumentCriterionVisitorsPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateContentSubdocumentMapperPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateContentTranslationSubdocumentMapperPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateFacetBuilderVisitorPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateSubdocumentQueryCriterionVisitorPass()); + $containerBuilder->addCompilerPass(new Compiler\RawFacetBuilderDomainVisitorPass()); + } +} diff --git a/tests/lib/Integration/SetupFactory/SolrAsynchronous.php b/tests/lib/Integration/SetupFactory/SolrAsynchronous.php new file mode 100644 index 00000000..1c2669f2 --- /dev/null +++ b/tests/lib/Integration/SetupFactory/SolrAsynchronous.php @@ -0,0 +1,45 @@ +load('search/common.yaml'); + $loader->load('search/solr_services.yaml'); + $loader->load('search/solr_engine.yaml'); + + $testConfigPath = __DIR__ . '/../Resources/config/'; + $loader = new YamlFileLoader($containerBuilder, new FileLocator($testConfigPath)); + $loader->load('services.yaml'); + $loader->load('event_dispatcher_override_asynchronous.yaml'); + + // Needs to be added first because other passes depend on it + $containerBuilder->addCompilerPass(new Compiler\TagSubdocumentCriterionVisitorsPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateContentSubdocumentMapperPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateContentTranslationSubdocumentMapperPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateFacetBuilderVisitorPass()); + $containerBuilder->addCompilerPass(new Compiler\AggregateSubdocumentQueryCriterionVisitorPass()); + $containerBuilder->addCompilerPass(new Compiler\RawFacetBuilderDomainVisitorPass()); + $containerBuilder->addCompilerPass(new Compiler\DescendantIndexingPass()); + + } +} diff --git a/tests/lib/Unit/Core/HierarchicalIndexing/AncestorPathGeneratorTest.php b/tests/lib/Unit/Core/HierarchicalIndexing/AncestorPathGeneratorTest.php new file mode 100644 index 00000000..bd662474 --- /dev/null +++ b/tests/lib/Unit/Core/HierarchicalIndexing/AncestorPathGeneratorTest.php @@ -0,0 +1,176 @@ + true, + ], + [], + ], + [ + [ + 'enabled' => true, + 'map' => [], + ], + [], + ], + [ + [ + 'enabled' => true, + 'map' => [ + 'cti_1' => [], + ], + ], + [], + ], + [ + [ + 'enabled' => true, + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'indexed' => true, + 'cti_2' => [], + ], + ], + ], + ], + [ + 'cti_2/cti_1', + ], + ], + [ + [ + 'enabled' => true, + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'indexed' => false, + 'cti_2' => [], + ], + ], + ], + ], + [ + 'cti_2/cti_1', + ], + ], + [ + [ + 'enabled' => true, + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'indexed' => false, + 'cti_2' => [ + 'children' => [ + 'indexed' => false, + 'cti_3' => [], + ], + ], + ], + ], + ], + ], + [ + 'cti_3/cti_2/cti_1', + 'cti_2/cti_1', + ], + ], + [ + [ + 'enabled' => true, + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'indexed' => false, + 'cti_2' => [ + 'children' => [ + 'indexed' => false, + 'cti_3' => [ + 'children' => [ + 'cti_4' => [] + ], + ], + ], + ], + ], + ], + ], + ], + [ + 'cti_4/cti_3/cti_2/cti_1', + 'cti_3/cti_2/cti_1', + 'cti_2/cti_1', + ], + ], + [ + [ + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'cti_2' => [ + 'children' => [ + 'indexed' => false, + 'cti_3' => [], + ], + ], + ], + ], + 'cti_4' => [ + 'children' => [ + 'indexed' => false, + 'cti_2' => [ + 'children' => [ + 'cti_3' => [], + ], + ], + ], + ], + ], + ], + [ + 'cti_3/cti_2/cti_1', + 'cti_2/cti_1', + 'cti_3/cti_2/cti_4', + 'cti_2/cti_4', + ], + ], + ]; + } + + /** + * @dataProvider providerForTestGetPaths + */ + public function testGetPaths(array $configuration, array $expectedPaths): void + { + $generator = $this->getAncestorPathGeneratorUnderTest($configuration); + + $actualPaths = $generator->getPaths(); + + self::assertSame($expectedPaths, $actualPaths); + } + + protected function getAncestorPathGeneratorUnderTest(array $configuration): AncestorPathGenerator + { + return new AncestorPathGenerator($configuration); + } +} diff --git a/tests/lib/Unit/Core/HierarchicalIndexing/AncestorResolverTest.php b/tests/lib/Unit/Core/HierarchicalIndexing/AncestorResolverTest.php new file mode 100644 index 00000000..a51b37d5 --- /dev/null +++ b/tests/lib/Unit/Core/HierarchicalIndexing/AncestorResolverTest.php @@ -0,0 +1,322 @@ + [ + 'cti_1' => [], + ], + ], + [ + [ + 'id' => 3, + 'parentId' => 2, + 'depth' => 4, + 'contentId' => 3, + 'contentTypeId' => 3, + 'contentTypeIdentifier' => 'cti_3', + ], + ], + 3, + null, + ], + [ + [ + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'cti_2' => [], + ], + ], + ], + ], + [ + [ + 'id' => 2, + 'parentId' => 1, + 'depth' => 3, + 'contentId' => 2, + 'contentTypeId' => 2, + 'contentTypeIdentifier' => 'cti_2', + ], + [ + 'id' => 1, + 'parentId' => 0, + 'depth' => 2, + 'contentId' => 1, + 'contentTypeId' => 1, + 'contentTypeIdentifier' => 'cti_1', + ], + ], + 2, + 1, + ], + [ + [ + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'cti_2' => [], + ], + ], + ], + ], + [ + [ + 'id' => 2, + 'parentId' => 1, + 'depth' => 3, + 'contentId' => 2, + 'contentTypeId' => 2, + 'contentTypeIdentifier' => 'cti_2', + ], + [ + 'id' => 1, + 'parentId' => 0, + 'depth' => 2, + 'contentId' => 1, + 'contentTypeId' => 1, + 'contentTypeIdentifier' => 'cti_5', + ], + ], + 2, + null, + ], + [ + [ + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'cti_2' => [ + 'children' => [ + 'cti_3' => [], + ], + ], + ], + ], + ], + ], + [ + [ + 'id' => 2, + 'parentId' => 1, + 'depth' => 3, + 'contentId' => 2, + 'contentTypeId' => 2, + 'contentTypeIdentifier' => 'cti_2', + ], + [ + 'id' => 1, + 'parentId' => 0, + 'depth' => 2, + 'contentId' => 1, + 'contentTypeId' => 1, + 'contentTypeIdentifier' => 'cti_1', + ], + ], + 2, + 1, + ], + [ + [ + 'map' => [ + 'cti_1' => [ + 'children' => [ + 'cti_2' => [ + 'children' => [ + 'cti_3' => [], + ], + ], + ], + ], + ], + ], + [ + [ + 'id' => 3, + 'parentId' => 2, + 'depth' => 4, + 'contentId' => 3, + 'contentTypeId' => 3, + 'contentTypeIdentifier' => 'cti_3', + ], + [ + 'id' => 2, + 'parentId' => 1, + 'depth' => 3, + 'contentId' => 2, + 'contentTypeId' => 2, + 'contentTypeIdentifier' => 'cti_2', + ], + [ + 'id' => 1, + 'parentId' => 0, + 'depth' => 2, + 'contentId' => 1, + 'contentTypeId' => 1, + 'contentTypeIdentifier' => 'cti_1', + ], + ], + 3, + 1, + ], + ]; + } + + /** + * @dataProvider providerForTestResolveAncestor + */ + public function testResolveAncestor( + array $configuration, + array $locationRepositoryData, + int $initialLocationId, + ?int $expectedAncestor, + ): void { + $resolver = $this->getAncestorResolverUnderTest($configuration, $locationRepositoryData); + + $locationStub = $this->getLocationStubFromRepositoryData($initialLocationId, $locationRepositoryData); + + $actualAncestor = $resolver->resolveAncestor($locationStub); + + if ($expectedAncestor === null) { + $this->assertNull($actualAncestor); + + return; + } + + $this->assertInstanceOf(Location::class, $actualAncestor); + $this->assertSame($expectedAncestor, $actualAncestor->id); + } + + protected function getAncestorResolverUnderTest(array $configuration, array $locationRepositoryData): AncestorResolver + { + return new AncestorResolver( + $this->getContentHandlerMock($locationRepositoryData), + $this->getContentTypeHandlerMock($locationRepositoryData), + $this->getLocationHandlerMock($locationRepositoryData), + new AncestorPathGenerator($configuration), + ); + } + + protected function getContentHandlerMock(array $locationRepositoryData): ContentHandlerInterface|MockObject + { + $mock = $this->getMockBuilder(ContentHandlerInterface::class)->getMock(); + $contentIdToContentTypeIdMap = []; + + foreach ($locationRepositoryData as $data) { + $contentIdToContentTypeIdMap[$data['contentId']] = $data['contentTypeId']; + } + + $mock->method('loadContentInfo')->willReturnCallback( + function ($id) use ($contentIdToContentTypeIdMap) { + foreach ($contentIdToContentTypeIdMap as $contentId => $contentTypeId) { + if ($id === $contentId) { + return new ContentInfo([ + 'contentTypeId' => $contentTypeId, + ]); + } + } + + throw new NotFoundException('ContentInfo', $id); + } + ); + + return $mock; + } + + protected function getContentTypeHandlerMock(array $locationRepositoryData): ContentTypeHandlerInterface|MockObject + { + $mock = $this->getMockBuilder(ContentTypeHandlerInterface::class)->getMock(); + $contentTypeIdToContentTypeIdentifierMap = []; + + foreach ($locationRepositoryData as $data) { + $contentTypeIdToContentTypeIdentifierMap[$data['contentTypeId']] = $data['contentTypeIdentifier']; + } + + $mock->method('load')->willReturnCallback( + function ($id) use ($contentTypeIdToContentTypeIdentifierMap) { + foreach ($contentTypeIdToContentTypeIdentifierMap as $contentTypeId => $contentTypeIdentifier) { + if ($id === $contentTypeId) { + return new Type([ + 'identifier' => $contentTypeIdentifier, + ]); + } + } + + throw new NotFoundException('ContentType', $id); + } + ); + + return $mock; + } + + protected function getLocationHandlerMock(array $locationRepositoryData): LocationHandlerInterface|MockObject + { + $mock = $this->getMockBuilder(LocationHandlerInterface::class)->getMock(); + + $mock->method('load')->willReturnCallback( + function ($id) use ($locationRepositoryData) { + foreach ($locationRepositoryData as $data) { + if ($id === $data['id']) { + return $this->getLocationStub($data); + } + } + + throw new NotFoundException('Location', $id); + } + ); + + return $mock; + } + + protected function getLocationStubFromRepositoryData(int $id, array $locationRepositoryData): Location + { + foreach ($locationRepositoryData as $data) { + if ($id === $data['id']) { + return $this->getLocationStub($data); + } + } + + throw new RuntimeException( + sprintf( + 'Missing Location #%s data', + $id, + ), + ); + } + + protected function getLocationStub(array $locationData): Location + { + return new Location([ + 'id' => $locationData['id'], + 'parentId' => $locationData['parentId'], + 'depth' => $locationData['depth'], + 'contentId' => $locationData['contentId'], + ]); + } +}