diff --git a/src/Entity/FacetSource.php b/src/Entity/FacetSource.php index 62b4ae9..b98578d 100644 --- a/src/Entity/FacetSource.php +++ b/src/Entity/FacetSource.php @@ -88,7 +88,7 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface { * {@inheritdoc} */ public function getFilterKey() { - return $this->filter_key; + return $this->filter_key ?: 'f'; } /** diff --git a/src/Form/CheckboxWidgetForm.php b/src/Form/CheckboxWidgetForm.php index 33beff9..e958359 100644 --- a/src/Form/CheckboxWidgetForm.php +++ b/src/Form/CheckboxWidgetForm.php @@ -96,40 +96,80 @@ class CheckboxWidgetForm implements BaseFormIdInterface { $values = $form_state->getValues(); $facet = $this->facet; - $result_link = FALSE; - $active_items = []; + $current_request = \Drupal::requestStack()->getCurrentRequest(); + $request_uri = $current_request->getRequestUri(); + + // The $base_request_uri is the current url before the query parameters. + // A url that looks like "search/content?f[0]=item:test" should like + // "search/content" in $base_request_uri. + if (strpos($request_uri, '?') !== FALSE) { + $base_request_uri = substr($request_uri, 0, strpos($request_uri, '?')); + } + else { + $base_request_uri = $request_uri; + } + $request_context = \Drupal::service('router.request_context'); + $base_url = $request_context->getCompleteBaseUrl(); + $base_request_uri = $base_url . $base_request_uri; + $active_items = []; foreach ($values[$facet->getFieldAlias()] as $key => $value) { if ($value !== 0) { $active_items[] = $value; } } + $active_results = []; foreach ($facet->getResults() as $result) { if (in_array($result->getRawValue(), $active_items)) { - $result_link = $result->getUrl(); + $active_results[] = $result; } } - // We have an active item, so we redirect to the page that has that facet + // We have an active item, so we redirect to the page that has those items // selected. This should be an absolute link because RedirectResponse is a // symfony class that requires a full URL. - if ($result_link instanceof Url) { - $result_link->setAbsolute(); - $form_state->setResponse(new RedirectResponse($result_link->toString())); + if (count($active_results) > 0) { + $arguments = []; + $filter_key = $facet->getFacetSourceConfig()->getFilterKey(); + + /** @var \Drupal\facets\Result\ResultInterface $link */ + foreach ($active_results as $k => $link) { + $arguments[] = $filter_key . '[' . $k . ']=' . $facet->getUrlAlias() . ':' . $link->getRawValue(); + } + + $url = $base_request_uri . '?' . implode('&', $arguments); + + $form_state->setResponse(new RedirectResponse($url)); return; } // The form was submitted but nothing was active in the form, we should - // still redirect, but the url for the new page can't come from a result. - // So we're redirecting to the facet source's page. - $path = $facet->getFacetSource()->getPath(); - if (substr($path, 0, 1) !== '/') { - $path = '/' . $path; + // still redirect, but remove the parameters for the current facet from the + // request. + $query_string = $current_request->getQueryString(); + $query_parts = explode('&', $query_string); + + // Loop over all url parameters, make sure that they are removed from the + // from the $query_parts array so that we can rebuild the url without those + // parts in the url. + foreach ($query_parts as $i => $part) { + if (strpos($part, $facet->getUrlAlias().'%3A') !== FALSE) { + unset($query_parts[$i]); + } + } + + // The query is empty, so just redirect to the request uri without anything + // in the query string. + if (count($query_parts) === 0) { + $form_state->setResponse(new RedirectResponse($base_request_uri)); + return; } - $link = Url::fromUserInput($path); - $link->setAbsolute(); - $form_state->setResponse(new RedirectResponse($link->toString())); + + // Add the query string to the url again to make sure that the redirect + // happens as expected. + $url = $base_request_uri . '?' . implode('&', $query_parts); + $form_state->setResponse(new RedirectResponse($url)); } } diff --git a/src/Tests/WidgetIntegrationTest.php b/src/Tests/WidgetIntegrationTest.php index 5f0a323..c599039 100644 --- a/src/Tests/WidgetIntegrationTest.php +++ b/src/Tests/WidgetIntegrationTest.php @@ -76,6 +76,85 @@ class WidgetIntegrationTest extends WebTestBase { } /** + * Tests submitting multiple items at the same time. + */ + public function testMultipleCheckboxItems() { + $id = 'palau'; + $name = 'Palau owl'; + $facet_add_page = 'admin/config/search/facets/add-facet'; + + // Create a new facet. + $this->drupalGet($facet_add_page); + $form_values = [ + 'id' => $id, + 'status' => 1, + 'url_alias' => $id, + 'name' => $name, + 'weight' => 7, + '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]' => 'keywords', + ]; + $this->drupalPostForm(NULL, ['facet_source_id' => 'search_api_views:search_api_test_view:page_1'], $this->t('Configure facet source')); + $this->drupalPostForm(NULL, $form_values, $this->t('Save')); + $this->drupalPostForm(NULL, ['widget' => 'checkbox'], $this->t('Save')); + + // Place a block. + $block_values = [ + 'plugin_id' => 'facet_block:' . $id, + 'settings' => [ + 'region' => 'footer', + 'id' => str_replace('_', '-', $id), + ], + ]; + $this->drupalPlaceBlock($block_values['plugin_id'], $block_values['settings']); + + // Go to the view, check that all results are displayed. + $this->drupalGet('search-api-test-fulltext'); + $this->assertText('Displaying 5 search results'); + + // Filter by the grape keyword. + $edit = ['keywords[grape]' => 'grape']; + $this->drupalPostForm(NULL, $edit, $this->t('submit')); + $this->assertText('Displaying 3 search results'); + $this->assertFieldChecked('edit-keywords-grape'); + + // Add apple to the filter as well. + $edit = ['keywords[grape]' => 'grape', 'keywords[apple]' => 'apple']; + $this->drupalPostForm(NULL, $edit, $this->t('submit')); + $this->assertText('Displaying 3 search results'); + $this->assertFieldChecked('edit-keywords-apple'); + $this->assertFieldChecked('edit-keywords-grape'); + + // Reset the filters back to default and make sure that the original 5 + // results are displayed again. + $this->drupalPostForm(NULL, ['keywords[grape]' => FALSE, 'keywords[apple]' => FALSE], $this->t('submit')); + $this->assertText('Displaying 5 search results'); + $this->assertNoFieldChecked('edit-keywords-apple'); + $this->assertNoFieldChecked('edit-keywords-grape'); + + // Filter on both grape and apple at once and check that those are now + // checked. + $this->drupalGet('search-api-test-fulltext'); + $this->assertText('Displaying 5 search results'); + $edit = ['keywords[grape]' => 'grape', 'keywords[apple]' => 'apple']; + $this->drupalPostForm(NULL, $edit, $this->t('submit')); + $this->assertText('Displaying 3 search results'); + $this->assertFieldChecked('edit-keywords-apple'); + $this->assertFieldChecked('edit-keywords-grape'); + } + + /** + * Tests submitting multiple items at the same time. + * + * This uses a configured facet source url. + */ + public function testMultipleCheckboxItems2() { + $this->drupalGet('admin/config/search/facets/facet-sources/search_api_views:search_api_test_view:page_1/edit'); + $this->drupalPostForm(NULL, ['filter_key' => 'q'], $this->t('Save')); + $this->testMultipleCheckboxItems(); + } + + /** * Tests multiple checkbox widgets. */ public function testMultipleCheckboxWidget() { diff --git a/src/UrlProcessor/UrlProcessorPluginBase.php b/src/UrlProcessor/UrlProcessorPluginBase.php index 9d6e9d6..aeeda34 100644 --- a/src/UrlProcessor/UrlProcessorPluginBase.php +++ b/src/UrlProcessor/UrlProcessorPluginBase.php @@ -62,7 +62,7 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url /** @var \Drupal\facets\FacetSourceInterface $facet_source_config */ $facet_source_config = $facet->getFacetSourceConfig(); - $this->filterKey = $facet_source_config->getFilterKey() ?: 'f'; + $this->filterKey = $facet_source_config->getFilterKey(); } /**