diff --git a/search_api.plugin_type.yml b/search_api.plugin_type.yml index c1b49c7..1fbd834 100644 --- a/search_api.plugin_type.yml +++ b/search_api.plugin_type.yml @@ -8,6 +8,11 @@ search_api_data_type: plugin_manager_service_id: plugin.manager.search_api.data_type plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator +search_api_display: + label: Search API display + plugin_manager_service_id: plugin.manager.search_api.display + plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator + search_api_processor: label: Search API processor plugin_manager_service_id: plugin.manager.search_api.processor diff --git a/search_api.services.yml b/search_api.services.yml index 6c1207a..61db9d4 100644 --- a/search_api.services.yml +++ b/search_api.services.yml @@ -18,6 +18,10 @@ services: class: Drupal\search_api\Datasource\DatasourcePluginManager parent: default_plugin_manager + plugin.manager.search_api.display: + class: Drupal\search_api\Display\DisplayPluginManager + parent: default_plugin_manager + plugin.manager.search_api.processor: class: Drupal\search_api\Processor\ProcessorPluginManager arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@string_translation'] diff --git a/src/Annotation/SearchApiDisplay.php b/src/Annotation/SearchApiDisplay.php new file mode 100644 index 0000000..0553ae5 --- /dev/null +++ b/src/Annotation/SearchApiDisplay.php @@ -0,0 +1,44 @@ +get('entity_type.manager'); + $deriver->setEntityTypeManager($entity_type_manager); + + $translation = $container->get('string_translation'); + $deriver->setStringTranslation($translation); + + return $deriver; + } + + /** + * Retrieves the entity manager. + * + * @return \Drupal\Core\Entity\EntityTypeManager + * The entity manager. + */ + public function getEntityTypeManager() { + return $this->entityTypeManager; + } + + /** + * Sets the entity manager. + * + * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager + * The entity manager. + * + * @return $this + */ + public function setEntityTypeManager(EntityTypeManager $entity_type_manager) { + $this->entityTypeManager = $entity_type_manager; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinition($derivative_id, $base_plugin_definition) { + $derivatives = $this->getDerivativeDefinitions($base_plugin_definition); + return isset($derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL; + } + + /** + * Compares two plugin definitions according to their labels. + * + * @param array $a + * A plugin definition, with at least a "label" key. + * @param array $b + * Another plugin definition. + * + * @return int + * An integer less than, equal to, or greater than zero if the first + * argument is considered to be respectively less than, equal to, or greater + * than the second. + */ + public function compareDerivatives(array $a, array $b) { + return strnatcasecmp($a['label'], $b['label']); + } + +} diff --git a/src/Display/DisplayInterface.php b/src/Display/DisplayInterface.php new file mode 100644 index 0000000..f5a0f24 --- /dev/null +++ b/src/Display/DisplayInterface.php @@ -0,0 +1,59 @@ +setCurrentPath($container->get('path.current')); + + return $display; + } + + /** + * Retrieves the current path service. + * + * @return CurrentPathStack + * The current path service. + */ + public function getCurrentPath() { + return $this->currentPath ?: \Drupal::service('path.current'); + } + + /** + * Sets the current path service. + * + * @param \Drupal\Core\Path\CurrentPathStack $current_path + * The new current path service. + * + * @return $this + */ + public function setCurrentPath(CurrentPathStack $current_path) { + $this->currentPath = $current_path; + return $this; + } + + /** + * {@inheritdoc} + */ + public function label() { + $plugin_definition = $this->getPluginDefinition(); + return $plugin_definition['label']; + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + $plugin_definition = $this->getPluginDefinition(); + return $plugin_definition['description']; + } + + /** + * {@inheritdoc} + */ + public function getIndex() { + $plugin_definition = $this->getPluginDefinition(); + return Index::load($plugin_definition['index']); + } + + /** + * {@inheritdoc} + */ + public function getPath() { + $plugin_definition = $this->getPluginDefinition(); + return Url::fromUserInput('/' . $plugin_definition['path']); + } + + /** + * {@inheritdoc} + */ + public function isRenderedInCurrentRequest() { + $plugin_definition = $this->getPluginDefinition(); + if (isset($plugin_definition['path'])) { + $current_path = $this->getCurrentPath()->getPath(); + return $current_path == $plugin_definition['path']; + } + return FALSE; + } + +} diff --git a/src/Display/DisplayPluginManager.php b/src/Display/DisplayPluginManager.php new file mode 100644 index 0000000..ff7739e --- /dev/null +++ b/src/Display/DisplayPluginManager.php @@ -0,0 +1,57 @@ +setCacheBackend($cache_backend, 'search_api_displays'); + } + + /** + * Returns all known displays. + * + * @return \Drupal\search_api\Display\DisplayInterface[] + * An array of display plugins, keyed by type identifier. + */ + public function getInstances() { + if ($this->displays === NULL) { + $this->displays = array(); + + foreach ($this->getDefinitions() as $name => $display_definition) { + if (class_exists($display_definition['class']) && empty($this->displays[$name])) { + $display = $this->createInstance($name); + $this->displays[$name] = $display; + } + } + } + + return $this->displays; + } + +} diff --git a/src/Plugin/search_api/display/ViewsPageDisplay.php b/src/Plugin/search_api/display/ViewsPageDisplay.php new file mode 100644 index 0000000..44ec510 --- /dev/null +++ b/src/Plugin/search_api/display/ViewsPageDisplay.php @@ -0,0 +1,15 @@ +entityTypeManager->getStorage('view'); + $all_views = $views_storage->loadMultiple(); + } + catch (PluginNotFoundException $e) { + return array(); + } + + if (!isset($this->derivatives[$base_plugin_id])) { + $plugin_derivatives = array(); + + /** @var \Drupal\views\Entity\View $view */ + foreach ($all_views as $view) { + $index = SearchApiQuery::getIndexFromTable($view->get('base_table')); + if ($index) { + $displays = $view->get('display'); + foreach ($displays as $name => $display_info) { + if ($display_info['display_plugin'] == "page") { + $machine_name = $base = $view->id() . '__' . $name; + // Make sure the machine name is unique. (Will almost always be + // the case, unless a view or page ID contains two consecutive + // underscores.) + $i = 0; + while (isset($plugin_derivatives[$machine_name])) { + $machine_name = $base . '_' . ++$i; + } + + $label_arguments = array( + '%view_name' => $view->label(), + '%display_title' => $display_info['display_title'] + ); + $label = $this->t('View %view_name, display %display_title', $label_arguments); + + $executable = $view->getExecutable(); + $executable->setDisplay($name); + $display = $executable->getDisplay(); + $plugin_derivatives[$machine_name] = array( + 'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name, + 'label' => $label, + 'description' => $view->get('description') ? $this->t('%view_description - Represents the page display %display_title of view %view_name.', array('%view_name' => $view->label(), '%view_description' => $view->get('description'), '%display_title' => $display_info['display_title'])) : $this->t('Represents the page display %display_title of view %view_name.', array('%view_name' => $view->label(), '%display_title' => $display_info['display_title'])), + 'view_id' => $view->id(), + 'view_display' => $name, + 'path' => $display->getPath(), + 'index' => $index->id(), + ) + $base_plugin_definition; + + $arguments = array( + '%view' => $view->label(), + '%display' => $display_info['display_title'], + ); + $sources[] = $this->t('View name: %view. Display: %display', $arguments); + } + } + } + } + uasort($plugin_derivatives, array($this, 'compareDerivatives')); + + $this->derivatives[$base_plugin_id] = $plugin_derivatives; + } + return $this->derivatives[$base_plugin_id]; + } + +} diff --git a/src/Plugin/views/query/SearchApiQuery.php b/src/Plugin/views/query/SearchApiQuery.php index 805d345..81420b4 100644 --- a/src/Plugin/views/query/SearchApiQuery.php +++ b/src/Plugin/views/query/SearchApiQuery.php @@ -199,6 +199,15 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o $this->query->addTag('views_' . $view->id()); $this->query->setOption('search_api_view', $view); + // We only provide a display plugin for Views page displays. + // @todo figure out how to allow new displays for other views display + // types to be added. Do we need a hook for this? + if ($display->getPluginId() == 'page') { + // Load the Search API display and attach it to the query. + $display_plugin_manager = \Drupal::service('plugin.manager.search_api.display'); + $search_api_display = $display_plugin_manager->createInstance('views_page:' . $view->id() . '__' . $view->current_display); + $this->query->setOption('search_api_display', $search_api_display); + } } catch (\Exception $e) { $this->abort($e->getMessage()); diff --git a/src/Tests/ViewsTest.php b/src/Tests/ViewsTest.php index 8002dc3..2afb29a 100644 --- a/src/Tests/ViewsTest.php +++ b/src/Tests/ViewsTest.php @@ -3,6 +3,7 @@ namespace Drupal\search_api\Tests; use Drupal\Component\Utility\Html; +use Drupal\Core\Url; use Drupal\search_api\Entity\Index; use Drupal\search_api\Utility; @@ -169,6 +170,14 @@ public function testView() { 'keywords_op' => '>=', ); $this->checkResults($query, array(1, 5), 'Search with arguments and filters', 'entity:entity_test/all/orange'); + + // Make sure there was a display plugin created for this view. + $displays = \Drupal::getContainer()->get('plugin.manager.search_api.display')->getInstances(); + $display_id = 'views_page:search_api_test_view__page_1'; + $this->assertEqual(array($display_id), array_keys($displays), 'A display plugin was created for the test view.'); + $view_url = Url::fromUserInput('/search-api-test')->toString(); + $this->assertEqual($view_url, $displays[$display_id]->getPath()->toString(), 'Display returns the correct path.'); + $this->assertEqual('database_search_index', $displays[$display_id]->getIndex()->id(), 'Display returns the correct search index.'); } /**