Commit cf0ba7a1 authored by Nick_vh's avatar Nick_vh Committed by borisson_

Issue #2794745 by borisson_, mkalkbrenner, mpp, Nick_vh, drunken monkey,...

Issue #2794745 by borisson_, mkalkbrenner, mpp, Nick_vh, drunken monkey, sam0971: Use Search API's display plugin to fix facets
parent 93ee47d9
......@@ -12,6 +12,7 @@ use Drupal\facets\Entity\FacetSource;
use Drupal\search_api\Query\QueryInterface;
use Drupal\views\Entity\View;
use Drupal\Core\Entity\EntityInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* Implements hook_help().
......@@ -79,8 +80,9 @@ function facets_entity_presave(EntityInterface $entity) {
$sources = [];
foreach ($entity->original->get('display') as $k => $display) {
// Check if the current display is also a facet source plugin and that
// is removed from the view.
$test = 'search_api_views:' . $entity->id() . ':' . $display['id'];
// is removed from the view. We use the double underscore here to make
// sure that we use core convention of "plugin:derived_plugin".
$test = 'views_page:' . $entity->id() . '__' . $display['id'];
if (array_key_exists($test, $definitions) && !array_key_exists($k, $entity->get('display'))) {
$entity_id = str_replace(':', '__', $test);
$source_entity = FacetSource::load($entity_id);
......@@ -122,3 +124,40 @@ function facets_preprocess_block(&$variables) {
$variables['attributes']['class'][] = 'block-facet-widget--' . Html::cleanCssIdentifier($facet->getWidget()['type']);
}
}
/**
* Implements hook_entity_predelete().
*
* We implement this hook to make sure that facet source plugins are cleared
* when a view is deleted. It also deletes facets that are created on those
* plugins.
*/
function facets_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity instanceof View) {
$facet_source_plugin_manager = \Drupal::getContainer()
->get('plugin.manager.facets.facet_source');
$definitions = $facet_source_plugin_manager->getDefinitions();
if (!is_array($definitions)) {
return;
}
foreach ($definitions as $plugin_id => $definition) {
if (strpos($plugin_id, 'views_page:' . $entity->id() . '__') !== FALSE) {
try {
$facetManager = \Drupal::getContainer()->get('facets.manager');
} catch (ServiceNotFoundException $e) {
\Drupal::logger('facets')->log(Drupal\Core\Logger\RfcLogLevel::DEBUG, 'Facet manager not found on trying to delete a view.');
return;
}
$facets = $facetManager->getFacetsByFacetSourceId($plugin_id);
foreach ($facets as $facet) {
$facet->delete();
}
}
}
// Clear cached plugin definitions for facet source to make sure we don't
// show stale data.
$facet_source_plugin_manager->clearCachedDefinitions();
}
}
......@@ -80,7 +80,7 @@ class FacetsSerializer extends Serializer {
}
// Processing facets.
$facetsource_id = "search_api_views:{$this->view->id()}:{$this->view->getDisplay()->display['id']}";
$facetsource_id = "views_page:{$this->view->id()}__{$this->view->getDisplay()->display['id']}";
$facets = $this->facetsManager->getFacetsByFacetSourceId($facetsource_id);
$this->facetsManager->updateResults($facetsource_id);
......
......@@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\search_api\Display\DisplayPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -30,20 +31,33 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
*/
protected $entityTypeManager;
/**
* The search api display plugin manager.
*
* @var \Drupal\search_api\Display\DisplayPluginManager
*/
protected $searchApiDisplayPluginManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
$deriver = new static();
/** @var \Drupal\Core\Entity\EntityTypeManager $entity_type_manager */
$module_list = $container->get('module_handler')->getModuleList();
if (!in_array('search_api', array_keys($module_list))) {
return;
}
$entity_type_manager = $container->get('entity_type.manager');
$deriver->setEntityTypeManager($entity_type_manager);
/** @var \Drupal\Core\StringTranslation\TranslationInterface $translation */
$translation = $container->get('string_translation');
$deriver->setStringTranslation($translation);
$search_api_display_plugin_manager = $container->get('plugin.manager.search_api.display');
$deriver->setSearchApiDisplayPluginManager($search_api_display_plugin_manager);
return $deriver;
}
......@@ -95,4 +109,24 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
return strnatcasecmp($a['label'], $b['label']);
}
/**
* Sets search api's display plugin manager.
*
* @param \Drupal\search_api\Display\DisplayPluginManager $search_api_display_plugin_manager
* The plugin manager.
*/
public function setSearchApiDisplayPluginManager(DisplayPluginManager $search_api_display_plugin_manager) {
$this->searchApiDisplayPluginManager = $search_api_display_plugin_manager;
}
/**
* Returns the display plugin manager.
*
* @return \Drupal\search_api\Display\DisplayPluginManager
* The plugin manager.
*/
public function getSearchApiDisplayPluginManager() {
return $this->searchApiDisplayPluginManager;
}
}
......@@ -9,13 +9,10 @@ use Drupal\views\Entity\View;
use Drupal\views\Views;
/**
* A facet source to support search api views.
*
* This facet source only supports views that have a search api index as a base,
* and only those displays that are a block or a page.
* A facet source to support search api views trough display plugins.
*
* @FacetsFacetSource(
* id = "search_api_views",
* id = "views_page",
* deriver = "Drupal\facets\Plugin\facets\facet_source\SearchApiViewsDeriver"
* )
*/
......@@ -97,7 +94,7 @@ class SearchApiViews extends SearchApiBaseFacetSource implements SearchApiFacetS
$results = $this->searchApiQueryHelper->getResults($this->pluginId);
// If our results are not there, execute the view to get the results.
if (!$results) {
if ($results === NULL) {
// If there are no results, execute the view. and check for results again!
$view = Views::getView($this->pluginDefinition['view_id']);
$view->setDisplay($this->pluginDefinition['view_display']);
......@@ -106,10 +103,12 @@ class SearchApiViews extends SearchApiBaseFacetSource implements SearchApiFacetS
}
// Get the results from the cache. It is possible it still errored out.
// @todo figure out what to do when this errors out.
if ($results instanceof ResultSetInterface) {
// Get our facet data.
$facet_results = $results->getExtraData('search_api_facets');
if ($facet_results === []) {
return;
}
// Loop over each facet and execute the build method from the given
// query type.
......@@ -138,7 +137,8 @@ class SearchApiViews extends SearchApiBaseFacetSource implements SearchApiFacetS
case 'page':
$request = \Drupal::requestStack()->getMasterRequest();
if ($request->attributes->get('_controller') === 'Drupal\views\Routing\ViewPageController::handle') {
list(, $search_api_view_id, $search_api_view_display) = explode(':', $this->getPluginId());
list(, $view) = explode(':', $this->getPluginId());
list($search_api_view_id, $search_api_view_display) = explode('__', $view);
if ($request->attributes->get('view_id') == $search_api_view_id && $request->attributes->get('display_id') == $search_api_view_display) {
return TRUE;
......
......@@ -2,17 +2,14 @@
namespace Drupal\facets\Plugin\facets\facet_source;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Plugin\PluginBase;
use Drupal\facets\FacetSource\FacetSourceDeriverBase;
/**
* Derives a facet source plugin definition for every Search API view.
* Derives a facet source plugin definition for every Search API display plugin.
*
* This facet source only supports views that have a search api index as a base,
* and only those displays that are a block or a page.
* This facet source supports all search api display sources.
*
* @see \Drupal\facets\Plugin\facets\facet_source\SearchApiViews
* @see \Drupal\facets\Plugin\facets\facet_source\SearchApi
*/
class SearchApiViewsDeriver extends FacetSourceDeriverBase {
......@@ -22,54 +19,27 @@ class SearchApiViewsDeriver extends FacetSourceDeriverBase {
public function getDerivativeDefinitions($base_plugin_definition) {
$base_plugin_id = $base_plugin_definition['id'];
try {
/** @var \Drupal\Core\Entity\EntityStorageInterface $views_storage */
$views_storage = $this->entityTypeManager->getStorage('view');
$all_views = $views_storage->loadMultiple();
}
catch (PluginNotFoundException $e) {
return [];
}
$plugin_derivatives = array();
$search_api_displays = $this->getSearchApiDisplayPluginManager();
/** @var \Drupal\views\Entity\View $view */
foreach ($all_views as $view) {
// Hardcoded usage of Search API views, for now.
if (strpos($view->get('base_table'), 'search_api_index') !== FALSE) {
$displays = $view->get('display');
foreach ($displays as $display_id => $display_info) {
// We only support page, block and REST export displays for views.
$allowed_plugins = ['page', 'block', 'rest_export'];
if (in_array($display_info['display_plugin'], $allowed_plugins)) {
$machine_name = $view->id() . PluginBase::DERIVATIVE_SEPARATOR . $display_id;
$plugin_derivatives = array();
foreach ($search_api_displays->getDefinitions() as $display) {
$machine_name = $display['view_id'] . '__' . $display['view_display'];
$label_arguments = [
'%view_name' => $view->label(),
'%display_title' => $display_info['display_title'],
'%display_type' => $display_info['display_plugin'],
];
$plugin_derivatives[$machine_name] = [
'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
'label' => $this->t('Search API view: %view_name, display: %display_title (%display_type)',
$label_arguments
),
'id' => $base_plugin_id . ':' . $machine_name,
'label' => $display['label'],
'description' => $this->t('Provides a facet source.'),
'view_id' => $view->id(),
'view_display' => $display_id,
'view_id' => $display['view_id'],
'view_display' => $display['view_display'],
] + $base_plugin_definition;
$sources[] = $this->t(
'Search API view: %view, display: %display',
[
'%view' => $view->label(),
'%display' => $display_info['display_title'],
]
);
}
}
}
$arguments = [
'%view' => $display['label'],
'%display' => $display['view_display'],
];
$sources[] = $this->t('Search API view: %view, display: %display', $arguments);
}
uasort($plugin_derivatives, array($this, 'compareDerivatives'));
$this->derivatives[$base_plugin_id] = $plugin_derivatives;
......
......@@ -82,6 +82,7 @@ class SearchApiString extends QueryTypePluginBase {
}
$this->facet->setResults($facet_results);
}
return $this->facet;
}
......
......@@ -33,12 +33,12 @@ trait BlockTestTrait {
$this->drupalGet($facet_add_page);
$facet_source = "search_api_views:{$source}:{$display_id}";
$facet_source = "views_page:{$source}__{$display_id}";
$form_values = [
'id' => $id,
'name' => $name,
'facet_source_id' => $facet_source,
"facet_source_configs[search_api_views:{$source}:{$display_id}][field_identifier]" => $field,
"facet_source_configs[views_page:{$source}__{$display_id}][field_identifier]" => $field,
];
$this->drupalPostForm(NULL, ['facet_source_id' => $facet_source], $this->t('Configure facet source'));
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
......
......@@ -49,7 +49,7 @@ class FacetSourceTest extends WebTestBase {
$this->assertResponse(200);
$this->assertUrl('admin/config/search/facets');
$this->assertText('Facet source search_api_views:search_api_test_view:block_1 has been saved.');
$this->assertText('Facet source views_page:search_api_test_view__block_1 has been saved.');
$this->clickLink($this->t('Configure'));
// Test that saving worked filter_key has the new value.
......@@ -72,7 +72,7 @@ class FacetSourceTest extends WebTestBase {
$this->assertResponse(200);
$this->assertUrl('admin/config/search/facets');
$this->assertText('Facet source search_api_views:search_api_test_view:block_1 has been saved.');
$this->assertText('Facet source views_page:search_api_test_view__block_1 has been saved.');
$this->clickLink($this->t('Configure'));
// Test that saving worked and that the url processor has the new value.
......
......@@ -138,6 +138,7 @@ class IntegrationTest extends WebTestBase {
$facet_id = 'bvf';
$this->createFacet($facet_name, $facet_id, 'type', 'block_1');
$this->drupalPostForm(NULL, ['facet_settings[only_visible_when_facet_source_is_visible]' => FALSE], 'Save');
// Place the views block in the footer of all pages.
$block_settings = [
......@@ -373,13 +374,13 @@ class IntegrationTest extends WebTestBase {
// Configure the facet source by selecting one of the Search API views.
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'search_api_views:search_api_test_view:page_1'], $this->t('Configure facet source'));
$this->drupalPostForm(NULL, ['facet_source_id' => 'views_page:search_api_test_view__page_1'], $this->t('Configure facet source'));
// Fill in all fields and make sure the 'field is required' message is no
// longer shown.
$facet_source_form = [
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => 'type',
'facet_source_id' => 'views_page:search_api_test_view__page_1',
'facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]' => 'type',
];
$this->drupalPostForm(NULL, $facet_source_form, $this->t('Save'));
......@@ -567,8 +568,8 @@ class IntegrationTest extends WebTestBase {
$this->assertResponse(200);
// Check that the expected facet sources and the owl facet are shown.
$this->assertText('search_api_views:search_api_test_view:block_1');
$this->assertText('search_api_views:search_api_test_view:page_1');
$this->assertText('views_page:search_api_test_view__block_1');
$this->assertText('views_page:search_api_test_view__page_1');
$this->assertText($name);
// Delete the view on which both facet sources are based.
......@@ -579,8 +580,8 @@ class IntegrationTest extends WebTestBase {
// and the facet/facet source are deleted.
$this->drupalGet('/admin/config/search/facets');
$this->assertResponse(200);
$this->assertNoText('search_api_views:search_api_test_view:page_1');
$this->assertNoText('search_api_views:search_api_test_view:block_1');
$this->assertNoText('views_page:search_api_test_view__page_1');
$this->assertNoText('views_page:search_api_test_view__block_1');
$this->assertNoText($name);
}
......@@ -607,8 +608,8 @@ class IntegrationTest extends WebTestBase {
$this->assertResponse(200);
// Check that the expected facet sources and the owl facet are shown.
$this->assertText('search_api_views:search_api_test_view:block_1');
$this->assertText('search_api_views:search_api_test_view:page_1');
$this->assertText('views_page:search_api_test_view__block_1');
$this->assertText('views_page:search_api_test_view__page_1');
$this->assertText($name);
// Delete the view display for the page.
......@@ -620,8 +621,8 @@ class IntegrationTest extends WebTestBase {
// and the facet/facet source are deleted.
$this->drupalGet('/admin/config/search/facets');
$this->assertResponse(200);
$this->assertNoText('search_api_views:search_api_test_view:page_1');
$this->assertText('search_api_views:search_api_test_view:block_1');
$this->assertNoText('views_page:search_api_test_view__page_1');
$this->assertText('views_page:search_api_test_view__block_1');
$this->assertNoText($name);
}
......@@ -683,8 +684,8 @@ class IntegrationTest extends WebTestBase {
$this->assertNoText('Field:');
// Check that the expected facet sources are shown.
$this->assertText('search_api_views:search_api_test_view:block_1');
$this->assertText('search_api_views:search_api_test_view:page_1');
$this->assertText('views_page:search_api_test_view__block_1');
$this->assertText('views_page:search_api_test_view__page_1');
}
/**
......@@ -719,7 +720,7 @@ class IntegrationTest extends WebTestBase {
// Configure the facet source by selecting one of the Search API views.
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'search_api_views:search_api_test_view:page_1'], $this->t('Configure facet source'));
$this->drupalPostForm(NULL, ['facet_source_id' => 'views_page:search_api_test_view__page_1'], $this->t('Configure facet source'));
// The field is still required.
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
......@@ -728,8 +729,8 @@ class IntegrationTest extends WebTestBase {
// Fill in all fields and make sure the 'field is required' message is no
// longer shown.
$facet_source_form = [
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => $facet_type,
'facet_source_id' => 'views_page:search_api_test_view__page_1',
'facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]' => $facet_type,
];
$this->drupalPostForm(NULL, $form_values + $facet_source_form, $this->t('Save'));
$this->assertNoText('field is required.');
......@@ -756,10 +757,10 @@ class IntegrationTest extends WebTestBase {
$form_values = [
'name' => $facet_name,
'id' => $facet_id,
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_id' => 'views_page:search_api_test_view__page_1',
];
$facet_source_configs['facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]'] = $facet_type;
$facet_source_configs['facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]'] = $facet_type;
// Try to submit a facet with a duplicate machine name after form rebuilding
// via facet source submit.
......
......@@ -52,10 +52,10 @@ class WidgetJSTest extends JavascriptTestBase {
// Select one of the options from the facet source dropdown and wait for the
// result to show.
$page->selectFieldOption('edit-facet-source-id', 'search_api_views:search_api_test_view:page_1');
$page->selectFieldOption('edit-facet-source-id', 'views_page:search_api_test_view__page_1');
$this->getSession()->wait(6000, "jQuery('.facet-source-field-wrapper').length > 0");
$page->selectFieldOption('facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]', 'type');
$page->selectFieldOption('facet_source_configs[views_page:search_api_test_view__page_1][field_identifier]', 'type');
// Check that after choosing the field, the name is already filled in.
$field_value = $this->getSession()->getPage()->findField('edit-name')->getValue();
......@@ -74,7 +74,7 @@ class WidgetJSTest extends JavascriptTestBase {
'id' => $id,
'name' => strtoupper($id),
'url_alias' => $id,
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_id' => 'views_page:search_api_test_view__page_1',
'field_identifier' => 'type',
'empty_behavior' => ['behavior' => 'none'],
'widget' => [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment