diff --git a/search_api.api.php b/search_api.api.php index 5ec34332..5739b37e 100644 --- a/search_api.api.php +++ b/search_api.api.php @@ -101,7 +101,7 @@ function hook_search_api_data_type_info_alter(array &$data_type_definitions) { * Alter the available parse modes. * * @param array $parse_mode_definitions - * The definitions of the data type plugins. + * The definitions of the parse mode plugins. * * @see \Drupal\search_api\ParseMode\ParseModePluginBase */ @@ -271,7 +271,7 @@ function hook_search_api_items_indexed(\Drupal\search_api\IndexInterface $index, * @param \Drupal\search_api\Query\QueryInterface $query * The query that will be executed. */ -function hook_search_api_query_alter(\Drupal\search_api\Query\QueryInterface &$query) { +function hook_search_api_query_alter(\Drupal\search_api\Query\QueryInterface $query) { // Do not run for queries with a certain tag. if ($query->hasTag('example_tag')) { return; @@ -295,7 +295,7 @@ function hook_search_api_query_alter(\Drupal\search_api\Query\QueryInterface &$q * @param \Drupal\search_api\Query\QueryInterface $query * The query that will be executed. */ -function hook_search_api_query_TAG_alter(\Drupal\search_api\Query\QueryInterface &$query) { +function hook_search_api_query_TAG_alter(\Drupal\search_api\Query\QueryInterface $query) { // Exclude the node with ID 10 from the search results. $fields = $query->getIndex()->getFields(); foreach ($query->getIndex()->getDatasources() as $datasource_id => $datasource) { @@ -316,7 +316,7 @@ function hook_search_api_query_TAG_alter(\Drupal\search_api\Query\QueryInterface * @param \Drupal\search_api\Query\ResultSetInterface $results * The search results to alter. */ -function hook_search_api_results_alter(\Drupal\search_api\Query\ResultSetInterface &$results) { +function hook_search_api_results_alter(\Drupal\search_api\Query\ResultSetInterface $results) { $results->setExtraData('example_hook_invoked', microtime(TRUE)); } @@ -329,7 +329,7 @@ function hook_search_api_results_alter(\Drupal\search_api\Query\ResultSetInterfa * @param \Drupal\search_api\Query\ResultSetInterface $results * The search results to alter. */ -function hook_search_api_results_TAG_alter(\Drupal\search_api\Query\ResultSetInterface &$results) { +function hook_search_api_results_TAG_alter(\Drupal\search_api\Query\ResultSetInterface $results) { $results->setExtraData('example_hook_invoked', microtime(TRUE)); } diff --git a/src/Backend/BackendPluginManager.php b/src/Backend/BackendPluginManager.php index c498cc48..49deb46b 100644 --- a/src/Backend/BackendPluginManager.php +++ b/src/Backend/BackendPluginManager.php @@ -4,7 +4,9 @@ namespace Drupal\search_api\Backend; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\BackendPluginInfoAlterAlter; +use Drupal\search_api\Annotation\SearchApiBackend; +use Drupal\search_api\SearchApiPluginManager; /** * Manages search backend plugins. @@ -14,7 +16,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; * @see \Drupal\search_api\Backend\BackendPluginBase * @see plugin_api */ -class BackendPluginManager extends DefaultPluginManager { +class BackendPluginManager extends SearchApiPluginManager { /** * Constructs a BackendPluginManager object. @@ -28,9 +30,10 @@ class BackendPluginManager extends DefaultPluginManager { * The module handler. */ public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { - parent::__construct('Plugin/search_api/backend', $namespaces, $module_handler, 'Drupal\search_api\Backend\BackendInterface', 'Drupal\search_api\Annotation\SearchApiBackend'); - $this->setCacheBackend($cache_backend, 'search_api_backends'); + parent::__construct('Plugin/search_api/backend', $namespaces, $module_handler, BackendInterface::class, SearchApiBackend::class); $this->alterInfo('search_api_backend_info'); + $this->alterEvent(BackendPluginInfoAlterAlter::class); + $this->setCacheBackend($cache_backend, 'search_api_backends'); } } diff --git a/src/DataType/DataTypePluginManager.php b/src/DataType/DataTypePluginManager.php index 770dbb9e..29362f3c 100644 --- a/src/DataType/DataTypePluginManager.php +++ b/src/DataType/DataTypePluginManager.php @@ -4,7 +4,8 @@ namespace Drupal\search_api\DataType; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\DataTypePluginInfoAlterAlter; +use Drupal\search_api\SearchApiPluginManager; /** * Manages data type plugins. @@ -14,7 +15,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; * @see \Drupal\search_api\DataType\DataTypePluginBase * @see plugin_api */ -class DataTypePluginManager extends DefaultPluginManager { +class DataTypePluginManager extends SearchApiPluginManager { /** * Static cache for the data type definitions. @@ -51,6 +52,7 @@ class DataTypePluginManager extends DefaultPluginManager { $this->setCacheBackend($cache_backend, 'search_api_data_type'); $this->alterInfo('search_api_data_type_info'); + $this->alterEvent(DataTypePluginInfoAlterAlter::class); } /** diff --git a/src/Datasource/DatasourcePluginManager.php b/src/Datasource/DatasourcePluginManager.php index be9e684c..4c4cd255 100644 --- a/src/Datasource/DatasourcePluginManager.php +++ b/src/Datasource/DatasourcePluginManager.php @@ -4,7 +4,8 @@ namespace Drupal\search_api\Datasource; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\DataSourcePluginInfoAlter; +use Drupal\search_api\SearchApiPluginManager; /** * Manages datasource plugins. @@ -14,7 +15,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; * @see \Drupal\search_api\Datasource\DatasourcePluginBase * @see plugin_api */ -class DatasourcePluginManager extends DefaultPluginManager { +class DatasourcePluginManager extends SearchApiPluginManager { /** * Constructs a DatasourcePluginManager object. @@ -31,6 +32,7 @@ class DatasourcePluginManager extends DefaultPluginManager { parent::__construct('Plugin/search_api/datasource', $namespaces, $module_handler, 'Drupal\search_api\Datasource\DatasourceInterface', 'Drupal\search_api\Annotation\SearchApiDatasource'); $this->setCacheBackend($cache_backend, 'search_api_datasources'); $this->alterInfo('search_api_datasource_info'); + $this->alterEvent(DataSourcePluginInfoAlter::class); } } diff --git a/src/Display/DisplayPluginManager.php b/src/Display/DisplayPluginManager.php index abac905a..21fda657 100644 --- a/src/Display/DisplayPluginManager.php +++ b/src/Display/DisplayPluginManager.php @@ -4,7 +4,8 @@ namespace Drupal\search_api\Display; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\DisplaysAlter; +use Drupal\search_api\SearchApiPluginManager; /** * Manages display plugins. @@ -14,7 +15,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; * @see \Drupal\search_api\Display\DisplayPluginBase * @see plugin_api */ -class DisplayPluginManager extends DefaultPluginManager implements DisplayPluginManagerInterface { +class DisplayPluginManager extends SearchApiPluginManager { /** * Static cache for the display plugins. @@ -32,6 +33,7 @@ class DisplayPluginManager extends DefaultPluginManager implements DisplayPlugin parent::__construct('Plugin/search_api/display', $namespaces, $module_handler, 'Drupal\search_api\Display\DisplayInterface', 'Drupal\search_api\Annotation\SearchApiDisplay'); $this->setCacheBackend($cache_backend, 'search_api_displays'); $this->alterInfo('search_api_displays'); + $this->alterEvent(DisplaysAlter::CLASS); } /** diff --git a/src/Entity/Index.php b/src/Entity/Index.php index 7765e079..3be6234f 100644 --- a/src/Entity/Index.php +++ b/src/Entity/Index.php @@ -8,6 +8,8 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\search_api\Datasource\DatasourceInterface; +use Drupal\search_api\Event\ItemsIndexed; +use Drupal\search_api\Event\Reindex; use Drupal\search_api\IndexInterface; use Drupal\search_api\Item\FieldInterface; use Drupal\search_api\LoggerTrait; @@ -996,7 +998,13 @@ class Index extends ConfigEntityBase implements IndexInterface { // Since we've indexed items now, triggering reindexing would have some // effect again. Therefore, we reset the flag. $this->setHasReindexed(FALSE); - \Drupal::moduleHandler()->invokeAll('search_api_items_indexed', [$this, $processed_ids]); + + $description = sprintf('Use %s instead', ItemsIndexed::class); + \Drupal::moduleHandler()->invokeAllDeprecated($description, 'search_api_items_indexed', [$this, $processed_ids]); + + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $dispatcher->dispatch(ItemsIndexed::NAME, new ItemsIndexed($this, $processed_ids)); // Clear search api list caches. Cache::invalidateTags(['search_api_list:' . $this->id]); @@ -1100,7 +1108,11 @@ class Index extends ConfigEntityBase implements IndexInterface { if ($this->status() && !$this->isReindexing()) { $this->setHasReindexed(); $this->getTrackerInstance()->trackAllItemsUpdated(); - \Drupal::moduleHandler()->invokeAll('search_api_index_reindex', [$this, FALSE]); + $description = sprintf('Use %s instead.', Reindex::class); + \Drupal::moduleHandler()->invokeAllDeprecated($description,'search_api_index_reindex', [$this, FALSE]); + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $dispatcher->dispatch(Reindex::NAME, new Reindex($this, FALSE)); } } @@ -1124,8 +1136,12 @@ class Index extends ConfigEntityBase implements IndexInterface { $this->getServerInstance()->deleteAllIndexItems($this); } if ($invoke_hook) { - \Drupal::moduleHandler() - ->invokeAll('search_api_index_reindex', [$this, !$this->isReadOnly()]); + $description = sprintf('Use %s instead.', Reindex::class); + \Drupal::moduleHandler()->invokeAllDeprecated($description, 'search_api_index_reindex', [$this, !$this->isReadOnly()]); + + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $dispatcher->dispatch(Reindex::NAME, new Reindex($this, !$this->isReadOnly())); } } @@ -1142,8 +1158,12 @@ class Index extends ConfigEntityBase implements IndexInterface { $index_task_manager->stopTracking($this); $index_task_manager->startTracking($this); $this->setHasReindexed(); + $description = sprintf('Use %s instead.', Reindex::class); \Drupal::moduleHandler() - ->invokeAll('search_api_index_reindex', [$this, FALSE]); + ->invokeAllDeprecated($description, 'search_api_index_reindex', [$this, FALSE]); + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $dispatcher->dispatch(Reindex::NAME, new Reindex($this, FALSE)); $index_task_manager->addItemsBatch($this); } diff --git a/src/Entity/Server.php b/src/Entity/Server.php index 61e858ea..035f3b40 100644 --- a/src/Entity/Server.php +++ b/src/Entity/Server.php @@ -5,6 +5,7 @@ namespace Drupal\search_api\Entity; use Drupal\Core\Cache\Cache; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\search_api\Event\ServerFeaturesAlter; use Drupal\search_api\IndexInterface; use Drupal\search_api\LoggerTrait; use Drupal\search_api\Query\QueryInterface; @@ -207,8 +208,12 @@ class Server extends ConfigEntityBase implements ServerInterface { if ($this->hasValidBackend()) { $this->features = $this->getBackend()->getSupportedFeatures(); } + $description = sprintf('Use %s instead.', ServerFeaturesAlter::class); \Drupal::moduleHandler() - ->alter('search_api_server_features', $this->features, $this); + ->alterDeprecated($description, 'search_api_server_features', $this->features, $this); + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $eventDispatcher->dispatch(ServerFeaturesAlter::NAME, new ServerFeaturesAlter($this->features, $this)); } return $this->features; diff --git a/src/Event/BackendPluginInfoAlterAlter.php b/src/Event/BackendPluginInfoAlterAlter.php new file mode 100644 index 00000000..c0f5d5ed --- /dev/null +++ b/src/Event/BackendPluginInfoAlterAlter.php @@ -0,0 +1,22 @@ +getDefinitions(); + } + +} diff --git a/src/Event/DataSourcePluginInfoAlter.php b/src/Event/DataSourcePluginInfoAlter.php new file mode 100644 index 00000000..7f5e1b4a --- /dev/null +++ b/src/Event/DataSourcePluginInfoAlter.php @@ -0,0 +1,22 @@ +getDefinitions(); + } + +} diff --git a/src/Event/DataTypePluginInfoAlterAlter.php b/src/Event/DataTypePluginInfoAlterAlter.php new file mode 100644 index 00000000..4ae2dcc1 --- /dev/null +++ b/src/Event/DataTypePluginInfoAlterAlter.php @@ -0,0 +1,23 @@ +getDefinitions(); + } + +} diff --git a/src/Event/DisplaysAlter.php b/src/Event/DisplaysAlter.php new file mode 100644 index 00000000..5bfca55d --- /dev/null +++ b/src/Event/DisplaysAlter.php @@ -0,0 +1,24 @@ +getDefinitions(); + } + +} diff --git a/src/Event/FieldTypeMappingAlter.php b/src/Event/FieldTypeMappingAlter.php new file mode 100644 index 00000000..d9a14f90 --- /dev/null +++ b/src/Event/FieldTypeMappingAlter.php @@ -0,0 +1,43 @@ +fieldTypeMapping = &$fieldTypeMapping; + } + + /** + * Get a reference to the field type mapping. + * + * @return array + * An array mapping all known (and supported) Drupal data types to their + * corresponding Search API data types. A value of FALSE means that fields of + * that type should be ignored by the Search API. + */ + public function &getFieldTypeMapping() { + return $this->fieldTypeMapping; + } + +} diff --git a/src/Event/ItemsIndexed.php b/src/Event/ItemsIndexed.php new file mode 100644 index 00000000..ec0df72c --- /dev/null +++ b/src/Event/ItemsIndexed.php @@ -0,0 +1,62 @@ +index = $index; + $this->processedIds = $processedIds; + } + + /** + * Get the index that indexed the items. + * + * @return \Drupal\search_api\IndexInterface + * The used index. + */ + public function getIndex() { + return $this->index; + } + + /** + * Get the processed ids. + * + * @return int[] + * An array containing the successfully indexed items' IDs. + */ + public function getProcessedIds() { + return $this->processedIds; + } + +} diff --git a/src/Event/ParseModePluginInfoAlterAlter.php b/src/Event/ParseModePluginInfoAlterAlter.php new file mode 100644 index 00000000..5c6c0691 --- /dev/null +++ b/src/Event/ParseModePluginInfoAlterAlter.php @@ -0,0 +1,22 @@ +getDefinitions(); + } + +} diff --git a/src/Event/PluginInfoAlterEvent.php b/src/Event/PluginInfoAlterEvent.php new file mode 100644 index 00000000..09512cb2 --- /dev/null +++ b/src/Event/PluginInfoAlterEvent.php @@ -0,0 +1,39 @@ +definitions = &$definitions; + } + + /** + * Get a reference to the definitions. + * + * @return array + * Reference to the definitions. + */ + public function &getDefinitions() { + return $this->definitions; + } + +} diff --git a/src/Event/ProcessorPluginInfoAlterAlter.php b/src/Event/ProcessorPluginInfoAlterAlter.php new file mode 100644 index 00000000..ec7daa60 --- /dev/null +++ b/src/Event/ProcessorPluginInfoAlterAlter.php @@ -0,0 +1,22 @@ +getDefinitions(); + } + +} diff --git a/src/Event/QueryAlter.php b/src/Event/QueryAlter.php new file mode 100644 index 00000000..87743b1e --- /dev/null +++ b/src/Event/QueryAlter.php @@ -0,0 +1,42 @@ +query = $query; + } + + /** + * Get the query that will be executed. + * + * @return \Drupal\search_api\Query\QueryInterface + * The query that will be executed. + */ + public function getQuery() { + return $this->query; + } + +} diff --git a/src/Event/Reindex.php b/src/Event/Reindex.php new file mode 100644 index 00000000..b370bf94 --- /dev/null +++ b/src/Event/Reindex.php @@ -0,0 +1,62 @@ +index = $index; + $this->clear = $clear; + } + + /** + * Get the index scheduled for reindexing. + * + * @return \Drupal\search_api\IndexInterface + * The index scheduled for reindexing. + */ + public function getIndex() { + return $this->index; + } + + /** + * Get the boolean indicating whether the index was also cleared. + * + * @return bool + * Boolean indicating whether the index was also cleared. + */ + public function isClear() { + return $this->clear; + } + +} diff --git a/src/Event/ResultsAlter.php b/src/Event/ResultsAlter.php new file mode 100644 index 00000000..cb20bad9 --- /dev/null +++ b/src/Event/ResultsAlter.php @@ -0,0 +1,42 @@ +results = $results; + } + + /** + * Get the search results to alter. + * + * @return \Drupal\search_api\Query\ResultSetInterface + * The search results to alter. + */ + public function getResults() { + return $this->results; + } + +} diff --git a/src/Event/ServerFeaturesAlter.php b/src/Event/ServerFeaturesAlter.php new file mode 100644 index 00000000..dd3688f4 --- /dev/null +++ b/src/Event/ServerFeaturesAlter.php @@ -0,0 +1,62 @@ +features = &$features; + $this->server = $server; + } + + /** + * Get a reference to the features supported by the server's backend. + * + * @return array + * Reference to the features supported by the server's backend. + */ + public function &getFeatures() { + return $this->features; + } + + /** + * Get the search server in question. + * + * @return \Drupal\search_api\ServerInterface + * The search server in question. + */ + public function getServer() { + return $this->server; + } + +} diff --git a/src/Event/TrackerPluginInfoAlterAlter.php b/src/Event/TrackerPluginInfoAlterAlter.php new file mode 100644 index 00000000..a7b97ba8 --- /dev/null +++ b/src/Event/TrackerPluginInfoAlterAlter.php @@ -0,0 +1,22 @@ +getDefinitions(); + } + +} diff --git a/src/ParseMode/ParseModePluginManager.php b/src/ParseMode/ParseModePluginManager.php index d913d9a2..4f3c090a 100644 --- a/src/ParseMode/ParseModePluginManager.php +++ b/src/ParseMode/ParseModePluginManager.php @@ -4,7 +4,8 @@ namespace Drupal\search_api\ParseMode; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\ParseModePluginInfoAlterAlter; +use Drupal\search_api\SearchApiPluginManager; /** * Manages parse mode plugins. @@ -14,7 +15,7 @@ use Drupal\Core\Plugin\DefaultPluginManager; * @see \Drupal\search_api\ParseMode\ParseModePluginBase * @see plugin_api */ -class ParseModePluginManager extends DefaultPluginManager { +class ParseModePluginManager extends SearchApiPluginManager { /** * Constructs a ParseModePluginManager object. @@ -29,9 +30,9 @@ class ParseModePluginManager extends DefaultPluginManager { */ public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) { parent::__construct('Plugin/search_api/parse_mode', $namespaces, $module_handler, 'Drupal\search_api\ParseMode\ParseModeInterface', 'Drupal\search_api\Annotation\SearchApiParseMode'); - $this->setCacheBackend($cache_backend, 'search_api_parse_mode'); $this->alterInfo('search_api_parse_mode_info'); + $this->alterEvent(ParseModePluginInfoAlterAlter::class); } /** diff --git a/src/Processor/ProcessorPluginManager.php b/src/Processor/ProcessorPluginManager.php index e665310a..39126f83 100644 --- a/src/Processor/ProcessorPluginManager.php +++ b/src/Processor/ProcessorPluginManager.php @@ -4,9 +4,10 @@ namespace Drupal\search_api\Processor; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\search_api\Event\ProcessorPluginInfoAlterAlter; +use Drupal\search_api\SearchApiPluginManager; /** * Manages processor plugins. @@ -16,7 +17,7 @@ use Drupal\Core\StringTranslation\TranslationInterface; * @see \Drupal\search_api\Processor\ProcessorPluginBase * @see plugin_api */ -class ProcessorPluginManager extends DefaultPluginManager { +class ProcessorPluginManager extends SearchApiPluginManager { use StringTranslationTrait; @@ -37,6 +38,7 @@ class ProcessorPluginManager extends DefaultPluginManager { parent::__construct('Plugin/search_api/processor', $namespaces, $module_handler, 'Drupal\search_api\Processor\ProcessorInterface', 'Drupal\search_api\Annotation\SearchApiProcessor'); $this->setCacheBackend($cache_backend, 'search_api_processors'); $this->alterInfo('search_api_processor_info'); + $this->alterEvent(ProcessorPluginInfoAlterAlter::class); $this->setStringTranslation($translation); } diff --git a/src/Query/Query.php b/src/Query/Query.php index e415cc2c..462dbb68 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -6,6 +6,8 @@ use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\search_api\Display\DisplayPluginManagerInterface; +use Drupal\search_api\Event\QueryAlter; +use Drupal\search_api\Event\ResultsAlter; use Drupal\search_api\IndexInterface; use Drupal\search_api\ParseMode\ParseModeInterface; use Drupal\search_api\ParseMode\ParseModePluginManager; @@ -544,11 +546,17 @@ class Query implements QueryInterface { $this->index->preprocessSearchQuery($this); // Let modules alter the query. - $hooks = ['search_api_query']; + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $eventDispatcher->dispatch(QueryAlter::NAME, new QueryAlter($this)); + $description = sprintf('Use %s instead', QueryAlter::class); + $this->getModuleHandler()->alterDeprecated($description, 'search_api_query', $this); + $hooks = []; foreach ($this->tags as $tag) { $hooks[] = "search_api_query_$tag"; + $eventDispatcher->dispatch(QueryAlter::NAME . '.' . $tag, new QueryAlter($this)); } - $this->getModuleHandler()->alter($hooks, $this); + $this->getModuleHandler()->alterDeprecated($description, $hooks, $this); } } @@ -564,11 +572,17 @@ class Query implements QueryInterface { $this->index->postprocessSearchResults($this->results); // Let modules alter the results. + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $eventDispatcher->dispatch(ResultsAlter::NAME, new ResultsAlter($this->results)); $hooks = ['search_api_results']; foreach ($this->tags as $tag) { $hooks[] = "search_api_results_$tag"; + $eventName = sprintf('%s.%s', ResultsAlter::NAME, $tag); + $eventDispatcher->dispatch($eventName, new ResultsAlter($this->results)); } - $this->getModuleHandler()->alter($hooks, $this->results); + $description = sprintf('Use %s instead', ResultsAlter::class); + $this->getModuleHandler()->alterDeprecated($description, $hooks, $this->results); // Store the results in the static cache. $this->getQueryHelper()->addResults($this->results); diff --git a/src/SearchApiPluginManager.php b/src/SearchApiPluginManager.php new file mode 100644 index 00000000..7da45527 --- /dev/null +++ b/src/SearchApiPluginManager.php @@ -0,0 +1,49 @@ +alterEvent = $alterEvent; + } + + /** + * {@inheritdoc} + */ + protected function alterDefinitions(&$definitions) { + if ($this->alterHook && !$this->alterEvent) { + $this->moduleHandler->alter($this->alterHook, $definitions); + return; + } + + if ($this->alterHook) { + $alternative = sprintf('Use the %s event instead.', $this->alterEvent); + $this->moduleHandler->alterDeprecated($alternative, $this->alterHook, $definitions); + } + + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $event = new $this->alterEvent($definitions); + $dispatcher->dispatch($event::NAME, $event); + } + +} diff --git a/src/Tracker/TrackerPluginManager.php b/src/Tracker/TrackerPluginManager.php index 7221979e..7719344c 100644 --- a/src/Tracker/TrackerPluginManager.php +++ b/src/Tracker/TrackerPluginManager.php @@ -4,7 +4,8 @@ namespace Drupal\search_api\Tracker; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\search_api\Event\TrackerPluginInfoAlterAlter; +use Drupal\search_api\SearchApiPluginManager; use Drupal\search_api\Utility\Utility; /** @@ -16,7 +17,7 @@ use Drupal\search_api\Utility\Utility; * @see \Drupal\search_api\Tracker\TrackerPluginBase * @see plugin_api */ -class TrackerPluginManager extends DefaultPluginManager { +class TrackerPluginManager extends SearchApiPluginManager { /** * Constructs a TrackerPluginManager object. @@ -33,6 +34,7 @@ class TrackerPluginManager extends DefaultPluginManager { parent::__construct('Plugin/search_api/tracker', $namespaces, $module_handler, 'Drupal\search_api\Tracker\TrackerInterface', 'Drupal\search_api\Annotation\SearchApiTracker'); $this->setCacheBackend($cache_backend, 'search_api_trackers'); $this->alterInfo('search_api_tracker_info'); + $this->alterEvent(TrackerPluginInfoAlterAlter::class); } /** diff --git a/src/Utility/CommandHelper.php b/src/Utility/CommandHelper.php index 86b68f14..aa0dd439 100644 --- a/src/Utility/CommandHelper.php +++ b/src/Utility/CommandHelper.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\search_api\ConsoleException; +use Drupal\search_api\Event\Reindex; use Drupal\search_api\IndexBatchHelper; use Drupal\search_api\IndexInterface; use Drupal\search_api\SearchApiException; @@ -354,7 +355,11 @@ class CommandHelper implements LoggerAwareInterface { $reindexed_datasources[] = $datasource->label(); } } - $this->moduleHandler->invokeAll('search_api_index_reindex', [$index, FALSE]); + $description = sprintf('Use %s instead.', Reindex::class); + $this->moduleHandler->invokeAllDeprecated($description, 'search_api_index_reindex', [$index, FALSE]); + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + $dispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $dispatcher->dispatch(Reindex::NAME, new Reindex($index, FALSE)); $arguments = [ '!index' => $index->label(), '!datasources' => implode(', ', $reindexed_datasources), diff --git a/src/Utility/DataTypeHelper.php b/src/Utility/DataTypeHelper.php index e535cf3f..fe44fb9c 100644 --- a/src/Utility/DataTypeHelper.php +++ b/src/Utility/DataTypeHelper.php @@ -4,6 +4,7 @@ namespace Drupal\search_api\Utility; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\search_api\DataType\DataTypePluginManager; +use Drupal\search_api\Event\FieldTypeMappingAlter; use Drupal\search_api\IndexInterface; use Drupal\search_api\SearchApiException; @@ -125,7 +126,11 @@ class DataTypeHelper implements DataTypeHelperInterface { // Allow other modules to intercept and define what default type they want // to use for their data type. - $this->moduleHandler->alter('search_api_field_type_mapping', $mapping); + $description = sprintf('Use %s instead.', FieldTypeMappingAlter::class); + $this->moduleHandler->alterDeprecated($description, 'search_api_field_type_mapping', $mapping); + /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = \Drupal::getContainer()->get('event_dispatcher'); + $eventDispatcher->dispatch(FieldTypeMappingAlter::NAME, new FieldTypeMappingAlter($mapping)); $this->fieldTypeMapping = $mapping; } diff --git a/tests/search_api_test_events/search_api_test_events.info.yml b/tests/search_api_test_events/search_api_test_events.info.yml new file mode 100644 index 00000000..2bf1bcc3 --- /dev/null +++ b/tests/search_api_test_events/search_api_test_events.info.yml @@ -0,0 +1,8 @@ +name: 'Search API Events Test' +type: module +description: 'Support module for Search API tests, tests all the events.' +package: Testing +dependencies: + - search_api:search_api +core: 8.x +hidden: true diff --git a/tests/search_api_test_events/search_api_test_events.services.yml b/tests/search_api_test_events/search_api_test_events.services.yml new file mode 100644 index 00000000..9ac7d9ba --- /dev/null +++ b/tests/search_api_test_events/search_api_test_events.services.yml @@ -0,0 +1,7 @@ +services: + + search_api_test_events.event_listener: + class: \Drupal\search_api_test_events\EventListener + arguments: ['@messenger'] + tags: + - { name: event_subscriber } diff --git a/tests/search_api_test_events/src/EventListener.php b/tests/search_api_test_events/src/EventListener.php new file mode 100644 index 00000000..d584720f --- /dev/null +++ b/tests/search_api_test_events/src/EventListener.php @@ -0,0 +1,254 @@ +messenger = $messenger; + } + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() { + return [ + BackendPluginInfoAlterAlter::NAME => 'backendInfoAlter', + DataSourcePluginInfoAlter::NAME => 'dataSourceInfoAlter', + DataTypePluginInfoAlterAlter::NAME => 'dataTypeInfoAlter', + DisplaysAlter::NAME => 'displaysAlter', + FieldTypeMappingAlter::NAME => 'fieldTypeMappingAlter', + ItemsIndexed::NAME => 'itemsIndexed', + ParseModePluginInfoAlterAlter::NAME => 'parseModeInfoAlter', + ProcessorPluginInfoAlterAlter::NAME => 'processorInfoAlter', + QueryAlter::NAME => 'queryAlter', + QueryAlter::NAME . '.andrew_hill' => 'queryTagAlter', + Reindex::NAME => 'reindex', + ResultsAlter::NAME => 'resultsAlter', + ResultsAlter::NAME . '.andrew_hill' => 'resultsTagAlter', + ServerFeaturesAlter::NAME => 'serverFeaturesAlter', + TrackerPluginInfoAlterAlter::NAME => 'trackerInfoAlter', + ]; + } + + /** + * Event handler for the backend info alter event. + * + * @param \Drupal\search_api\Event\BackendPluginInfoAlterAlter $event + * The backend info alter event. + */ + public function backendInfoAlter(BackendPluginInfoAlterAlter $event) { + $backend_info = &$event->getBackendInfo(); + $backend_info['search_api_test']['label'] = 'Slims return'; + } + + /** + * Event handler for the data type info alter event. + * + * @param \Drupal\search_api\Event\DataTypePluginInfoAlterAlter $event + * The data type info alter event. + */ + public function dataTypeInfoAlter(DataTypePluginInfoAlterAlter $event) { + $dataTypePluginInfo = &$event->getDataTypePluginInfo(); + if (isset($dataTypePluginInfo['text'])) { + $dataTypePluginInfo['text']['label'] = 'Peace/Dolphin dance'; + } + } + + /** + * Event handler for the data source info alter event. + * + * @param \Drupal\search_api\Event\DataSourcePluginInfoAlter $event + * The data source info alter event. + */ + public function dataSourceInfoAlter(DataSourcePluginInfoAlter $event) { + $infos = &$event->getDataSourceInfo(); + if (isset($infos['entity:node'])) { + $infos['entity:node']['label'] = 'Distant land'; + } + } + + /** + * Event handler for the displays alter event. + * + * @param \Drupal\search_api\Event\DisplaysAlter $event + * The displays alter event. + */ + public function displaysAlter(DisplaysAlter $event) { + $displays = &$event->getDisplays(); + if (isset($displays['views_page:search_api_test_view__page_1'])) { + $displays['views_page:search_api_test_view__page_1']['label'] = 'Some funny label for testing'; + } + } + + /** + * Event handler for the field type mapping alter event. + * + * @param \Drupal\search_api\Event\FieldTypeMappingAlter $event + * The field type mapping alter event. + */ + public function fieldTypeMappingAlter(FieldTypeMappingAlter $event) { + $mapping = &$event->getFieldTypeMapping(); + $mapping['datetime_iso8601'] = FALSE; + $mapping['timestamp'] = FALSE; + } + + /** + * Event handler for the items indexed event. + * + * @param \Drupal\search_api\Event\ItemsIndexed $event + * The items indexed event. + */ + public function itemsIndexed(ItemsIndexed $event) { + $this->messenger->addStatus('Please set me at ease'); + } + + /** + * Event handler for the parse mode info alter event. + * + * @param \Drupal\search_api\Event\ParseModePluginInfoAlterAlter $event + * The parse mode plugin info alter event. + */ + public function parseModeInfoAlter(ParseModePluginInfoAlterAlter $event) { + $parseModeInfo = &$event->getParseModeInfo(); + if (isset($parseModeInfo['direct'])) { + $parseModeInfo['direct']['label'] = 'Song for My Father'; + } + } + + /** + * Event handler for the processor info alter event. + * + * @param \Drupal\search_api\Event\ProcessorPluginInfoAlterAlter $event + * The processor plugin info alter event. + */ + public function processorInfoAlter(ProcessorPluginInfoAlterAlter $event) { + $processorInfo = &$event->getProcessorInfo(); + $processorInfo['content_access']['label'] = 'Mystic bounce'; + } + + /** + * Event handler for the query alter event. + * + * @param \Drupal\search_api\Event\QueryAlter $event + * The query alter event. + */ + public function queryAlter(QueryAlter $event) { + $query = $event->getQuery(); + $this->messenger->addStatus('Funky blue note'); + $this->messenger->addStatus("Search id: {$query->getSearchId(FALSE)}"); + $query->addTag('andrew_hill'); + } + + /** + * Event handler for the query TAG alter event. + * + * @param \Drupal\search_api\Event\QueryAlter $event + * The query alter event. + */ + public function queryTagAlter(QueryAlter $event) { + $this->messenger->addStatus("Freeland"); + $query = $event->getQuery(); + // Exclude the node with ID 2 from the search results. + $query->setOption('tag query alter hook', TRUE); + $index = $query->getIndex(); + $fields = $index->getFields(); + foreach ($index->getDatasources() as $datasource_id => $datasource) { + if ($datasource->getEntityTypeId() === 'node') { + $field = Utility::createCombinedId($datasource_id, 'nid'); + if (isset($fields[$field])) { + $query->addCondition($field, 2, '<>'); + } + } + } + } + + /** + * Event handler for the reindex event. + * + * @param \Drupal\search_api\Event\Reindex $event + * The reindex index event. + */ + public function reindex(Reindex $event) { + $this->messenger->addStatus('Montara'); + } + + /** + * Event handler for the results alter event. + * + * @param \Drupal\search_api\Event\ResultsAlter $event + * The results alter event. + */ + public function resultsAlter(ResultsAlter $event) { + $this->messenger->addStatus('Stepping into tomorrow'); + } + + /** + * Event handler for the results TAG alter event. + * + * @param \Drupal\search_api\Event\ResultsAlter $event + * The results alter event. + */ + public function resultsTagAlter(ResultsAlter $event) { + $this->messenger->addStatus('Llama'); + } + + /** + * Event handler for the server features alter event. + * + * @param \Drupal\search_api\Event\ServerFeaturesAlter $event + * The server features alter event. + */ + public function serverFeaturesAlter(ServerFeaturesAlter $event) { + $features = &$event->getFeatures(); + $server = $event->getServer(); + if ($server->id() === 'webtest_server') { + $features[] = 'welcome_to_the_jungle'; + } + } + + /** + * Event handler for the tracker info alter event. + * + * @param \Drupal\search_api\Event\TrackerPluginInfoAlterAlter $event + * The tracker plugin info alter event. + */ + public function trackerInfoAlter(TrackerPluginInfoAlterAlter $event) { + $trackerInfo = &$event->getTrackerInfo(); + $trackerInfo['search_api_test']['label'] = 'Good luck'; + } + +} diff --git a/tests/search_api_test_hooks/search_api_test_hooks.module b/tests/search_api_test_hooks/search_api_test_hooks.module index 8aac8449..feddef93 100644 --- a/tests/search_api_test_hooks/search_api_test_hooks.module +++ b/tests/search_api_test_hooks/search_api_test_hooks.module @@ -12,7 +12,8 @@ use Drupal\search_api\Utility\Utility; /** * Implements hook_search_api_query_TAG_alter(). */ -function search_api_test_hooks_search_api_query_andrew_hill_alter(QueryInterface &$query) { +function search_api_test_hooks_search_api_query_andrew_hill_alter(QueryInterface $query) { + \Drupal::messenger()->addStatus("Freeland"); // Exclude the node with ID 2 from the search results. $query->setOption('tag query alter hook', TRUE); $index = $query->getIndex(); @@ -30,6 +31,6 @@ function search_api_test_hooks_search_api_query_andrew_hill_alter(QueryInterface /** * Implements hook_search_api_results_TAG_alter(). */ -function search_api_test_hooks_search_api_results_andrew_hill_alter(ResultSetInterface &$results) { +function search_api_test_hooks_search_api_results_andrew_hill_alter(ResultSetInterface $results) { \Drupal::messenger()->addStatus('Llama'); } diff --git a/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc b/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc index e9daf171..a48aa041 100644 --- a/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc +++ b/tests/search_api_test_hooks/search_api_test_hooks.search_api.inc @@ -83,7 +83,7 @@ function search_api_test_hooks_search_api_items_indexed(IndexInterface $index, a /** * Implements hook_search_api_query_alter(). */ -function search_api_test_hooks_search_api_query_alter(QueryInterface &$query) { +function search_api_test_hooks_search_api_query_alter(QueryInterface $query) { \Drupal::messenger()->addStatus('Funky blue note'); \Drupal::messenger()->addStatus("Search id: {$query->getSearchId(FALSE)}"); $query->addTag('andrew_hill'); @@ -92,7 +92,7 @@ function search_api_test_hooks_search_api_query_alter(QueryInterface &$query) { /** * Implements hook_search_api_results_alter(). */ -function search_api_test_hooks_search_api_results_alter(ResultSetInterface &$results) { +function search_api_test_hooks_search_api_results_alter(ResultSetInterface $results) { \Drupal::messenger()->addStatus('Stepping into tomorrow'); } diff --git a/tests/src/Functional/EventsTest.php b/tests/src/Functional/EventsTest.php new file mode 100644 index 00000000..e6c4f1d4 --- /dev/null +++ b/tests/src/Functional/EventsTest.php @@ -0,0 +1,162 @@ +drupalCreateNode(['type' => 'page', 'title' => 'node - 1']); + $this->drupalCreateNode(['type' => 'page', 'title' => 'node - 2']); + $this->drupalCreateNode(['type' => 'page', 'title' => 'node - 3']); + $this->drupalCreateNode(['type' => 'page', 'title' => 'node - 4']); + + // Create an index and server to work with. + $this->server = $this->getTestServer(); + $index = $this->getTestIndex(); + + // Add the test processor to the index so we can make sure that all expected + // processor methods are called, too. + /** @var \Drupal\search_api\Processor\ProcessorInterface $processor */ + $processor = \Drupal::getContainer() + ->get('search_api.plugin_helper') + ->createProcessorPlugin($index, 'search_api_test'); + $index->addProcessor($processor)->save(); + + // Parts of this test actually use the "database_search_index" from the + // search_api_test_db module (via the test view). Set the processor there, + // too. + $index = Index::load('database_search_index'); + $processor = \Drupal::getContainer() + ->get('search_api.plugin_helper') + ->createProcessorPlugin($index, 'search_api_test'); + $index->addProcessor($processor)->save(); + + // Reset the called methods on the processor. + $this->getCalledMethods('processor'); + + // Log in, so we can test all the things. + $this->drupalLogin($this->adminUser); + } + + /** + * Tests various operations via the Search API's admin UI. + */ + public function testEvents() { + // The BackendInfo event was invoked. + $this->drupalGet('admin/config/search/search-api/add-server'); + $this->assertSession()->pageTextContains('Slims return'); + + // The DatasourceInfo event was invoked. + $this->drupalGet('admin/config/search/search-api/add-index'); + $this->assertSession()->pageTextContains('Distant land'); + // The TrackerInfo event was invoked. + $this->assertSession()->pageTextContains('Good luck'); + + // The ProcessorInfo event was invoked. + $this->drupalGet($this->getIndexPath('processors')); + $this->assertSession()->pageTextContains('Mystic bounce'); + + // The ParseModeInfo event was invoked. + $definition = \Drupal::getContainer() + ->get('plugin.manager.search_api.parse_mode') + ->getDefinition('direct'); + $this->assertEquals('Song for My Father', $definition['label']); + + // Saving the index should trigger the processor's preIndexSave() method. + $this->submitForm([], 'Save'); + $processor_methods = $this->getCalledMethods('processor'); + $this->assertEquals(['preIndexSave'], $processor_methods); + + $this->drupalGet($this->getIndexPath()); + // Duplication on value 'Index now' with summary. + $this->submitForm([], 'Index now'); + $this->checkForMetaRefresh(); + $this->assertSession()->pageTextContains('Successfully indexed 4 items.'); + + // During indexing, alterIndexedItems() and preprocessIndexItems() should be + // called on the processor. + $processor_methods = $this->getCalledMethods('processor'); + $expected = ['alterIndexedItems', 'preprocessIndexItems']; + $this->assertEquals($expected, $processor_methods); + $this->assertSession()->pageTextContains('There are 2 items indexed on the server for this index.'); + + // The ItemsIndexed event was invoked. + $this->assertSession()->pageTextContains('Please set me at ease'); + + // The Reindex event was invoked. + $this->drupalGet($this->getIndexPath('reindex')); + $this->submitForm([], 'Confirm'); + $this->assertSession()->pageTextContains('Montara'); + + // The DataTypePluginInfo event was invoked. + $this->drupalGet($this->getIndexPath('fields')); + $this->assertSession()->pageTextContains('Peace/Dolphin dance'); + // The implementation of hook_search_api_field_type_mapping_alter() has + // removed all dates, so we can't see any timestamp anymore in the page. + $url_options['query']['datasource'] = 'entity:node'; + $this->drupalGet($this->getIndexPath('fields/add/nojs'), $url_options); + $this->assertSession()->pageTextContains('Add fields to index'); + $this->assertSession()->pageTextNotContains('timestamp'); + + // The QueryAlter event was invoked. + $this->drupalGet('search-api-test'); + $this->assertSession()->pageTextContains('Search id: views_page:search_api_test_view__page_1'); + $this->assertSession()->pageTextContains('Funky blue note'); + // The Query(TAG)Alter event was invoked, this removed node:2. + $this->assertSession()->pageTextContains('Freeland'); + // The ResultsAlter event was invoked. + $this->assertSession()->pageTextContains('Stepping into tomorrow'); + // THe Results(TAG)Alter event was invoked. + $this->assertSession()->pageTextContains('Llama'); + + // The query alter methods of the processor were called. + $processor_methods = $this->getCalledMethods('processor'); + $expected = ['preprocessSearchQuery', 'postprocessSearchResults']; + $this->assertEquals($expected, $processor_methods); + + // The ServerFeaturesAlter hook was invoked. + $this->assertTrue($this->server->supportsFeature('welcome_to_the_jungle')); + + $displays = \Drupal::getContainer()->get('plugin.manager.search_api.display') + ->getInstances(); + // The DisplaysAlter event was invoked. + $display_label = $displays['views_page:search_api_test_view__page_1']->label(); + $this->assertEquals('Some funny label for testing', $display_label); + } + +} diff --git a/tests/src/Functional/HooksTest.php b/tests/src/Functional/HooksTest.php index e2f863dc..417ec45e 100644 --- a/tests/src/Functional/HooksTest.php +++ b/tests/src/Functional/HooksTest.php @@ -115,7 +115,6 @@ class HooksTest extends SearchApiBrowserTestBase { $this->assertEquals($expected, $processor_methods); // hook_search_api_index_items_alter() was invoked, this removed node:1. - // hook_search_api_query_TAG_alter() was invoked, this removed node:3. $this->assertSession()->pageTextContains('There are 2 items indexed on the server for this index.'); $this->assertSession()->pageTextContains('Stormy'); @@ -141,6 +140,8 @@ class HooksTest extends SearchApiBrowserTestBase { $this->assertSession()->pageTextContains('Search id: views_page:search_api_test_view__page_1'); // hook_search_api_query_alter() was invoked. $this->assertSession()->pageTextContains('Funky blue note'); + // hook_search_api_query_TAG_alter() was invoked, this removed node:2. + $this->assertSession()->pageTextContains('Freeland'); // hook_search_api_results_alter() was invoked. $this->assertSession()->pageTextContains('Stepping into tomorrow'); // hook_search_api_results_TAG_alter() was invoked. diff --git a/tests/src/Kernel/System/QueryTest.php b/tests/src/Kernel/System/QueryTest.php index c5b27f14..f37e1471 100644 --- a/tests/src/Kernel/System/QueryTest.php +++ b/tests/src/Kernel/System/QueryTest.php @@ -114,6 +114,7 @@ class QueryTest extends KernelTestBase { MessengerInterface::TYPE_STATUS => [ 'Funky blue note', 'Search id: ', + 'Freeland', 'Stepping into tomorrow', 'Llama', ],