Contributed modules
This commit is contained in:
parent
6286c6b165
commit
e822299d2f
@ -16,7 +16,7 @@
|
||||
},
|
||||
"license": "GPL-2.0+",
|
||||
"require-dev": {
|
||||
"drupal/search_api": "~1.21",
|
||||
"drupal/search_api": "^1.24||1.x-dev",
|
||||
"drupal/jquery_ui_slider": "~1.1",
|
||||
"drupal/jquery_ui_touch_punch": "~1.0"
|
||||
},
|
||||
|
@ -14,6 +14,12 @@ facets.facet.*:
|
||||
min_count:
|
||||
type: integer
|
||||
label: 'Minimum count'
|
||||
missing:
|
||||
type: boolean
|
||||
label: 'Missing'
|
||||
missing_label:
|
||||
type: label
|
||||
label: 'Missing label'
|
||||
url_alias:
|
||||
type: label
|
||||
label: 'Name of facet as used in the URL'
|
||||
|
@ -1,14 +1,14 @@
|
||||
name: 'Facets'
|
||||
type: module
|
||||
description: 'Faceted search interfaces that can be used on Search API searchers.'
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
core_version_requirement: ^9.3 || ^10.0
|
||||
package: Search
|
||||
configure: entity.facets_facet.collection
|
||||
test_dependencies:
|
||||
- search_api:search_api
|
||||
- drupal:views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -8,6 +8,7 @@
|
||||
use Drupal\facets\Entity\Facet;
|
||||
use Drupal\facets\Entity\FacetSource;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay;
|
||||
|
||||
/**
|
||||
* Implements hook_update_dependencies().
|
||||
@ -211,6 +212,41 @@ function facets_update_8008() {
|
||||
* Resave facets for consistent configuration export.
|
||||
*/
|
||||
function facets_update_8009() {
|
||||
// Moved to facets_update_8011().
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable facet block caching for the views with "Search API tag or time" cache.
|
||||
*/
|
||||
function facets_update_8010() {
|
||||
$facet_storage = \Drupal::entityTypeManager()->getStorage('facets_facet');
|
||||
$processed_views = [];
|
||||
/** @var \Drupal\facets\FacetInterface $facet */
|
||||
foreach ($facet_storage->loadMultiple() as $facet) {
|
||||
if (
|
||||
($source = $facet->getFacetSource())
|
||||
&& $source instanceof SearchApiDisplay
|
||||
&& ($view_executable = $source->getViewsDisplay())
|
||||
&& !in_array($view_executable->id(), $processed_views)
|
||||
&& ($cache_plugin = $view_executable->getDisplay()->getPlugin('cache'))
|
||||
&& in_array(
|
||||
$cache_plugin->getPluginId(),
|
||||
['search_api_tag', 'search_api_time']
|
||||
)
|
||||
) {
|
||||
$view_executable->save();
|
||||
$processed_views[] = $view_executable->id();
|
||||
}
|
||||
}
|
||||
return !empty($processed_views)
|
||||
? sprintf('Facet caching was enabled for the following views: %s.', implode(', ', $processed_views))
|
||||
: 'There are no views with search API cache plugins and facets in the same time, so nothing has been updated.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resave facets for consistent configuration export.
|
||||
*/
|
||||
function facets_update_8011() {
|
||||
$facets = Facet::loadMultiple();
|
||||
foreach ($facets as $facet) {
|
||||
$facet->save();
|
||||
|
@ -10,10 +10,12 @@ use Drupal\Core\Breadcrumb\Breadcrumb;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\facets\Entity\Facet;
|
||||
use Drupal\facets\Entity\FacetSource;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\FacetSource\SearchApiFacetSourceInterface;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
@ -202,8 +204,18 @@ function facets_entity_predelete(EntityInterface $entity) {
|
||||
* @see https://www.drupal.org/node/1842756
|
||||
*/
|
||||
function facets_preprocess_facets_item_list(array &$variables) {
|
||||
if ($variables['facet'] !== NULL && $variables['facet']->get('show_title') === TRUE) {
|
||||
$variables['title'] = $variables['facet']->label();
|
||||
/** @var FacetInterface $facet */
|
||||
$facet = $variables['facet'];
|
||||
if ($facet !== NULL) {
|
||||
if ($facet->get('show_title') === TRUE) {
|
||||
$variables['title'] = $facet->label();
|
||||
}
|
||||
if (Settings::get('facets_debug_cacheable_metadata', FALSE) && $facet->getFacetSource() instanceof SearchApiFacetSourceInterface) {
|
||||
$variables['cache_hash'] = $hash = substr(base_convert(hash('sha256', uniqid(time())), 16, 36), 0, 6);
|
||||
$variables['cache_contexts'] = implode(', ', $facet->getCacheContexts());
|
||||
$variables['cache_tags'] = implode(', ', $facet->getCacheTags());
|
||||
$variables['cache_max_age'] = $facet->getCacheMaxAge();
|
||||
}
|
||||
}
|
||||
template_preprocess_item_list($variables);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ test_dependencies:
|
||||
- facets:facets
|
||||
- drupal:views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -12,7 +12,7 @@ test_dependencies:
|
||||
- drupal:views
|
||||
- drupal:rest
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -12,7 +12,7 @@ dependencies:
|
||||
- search_api:search_api_test_db
|
||||
- facets:facets_rest
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -10,7 +10,7 @@ test_dependencies:
|
||||
- facets:facets
|
||||
- drupal:views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -10,7 +10,7 @@ test_dependencies:
|
||||
- search_api:search_api
|
||||
- drupal:views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -292,9 +292,7 @@ class FacetsSummaryForm extends EntityForm {
|
||||
foreach ($processors_by_stage as $stage => $processors) {
|
||||
/** @var \Drupal\facets\Processor\ProcessorInterface $processor */
|
||||
foreach ($processors as $processor_id => $processor) {
|
||||
$weight = isset($processor_settings[$processor_id]['weights'][$stage])
|
||||
? $processor_settings[$processor_id]['weights'][$stage]
|
||||
: $processor->getDefaultWeight($stage);
|
||||
$weight = $processor_settings[$processor_id]['weights'][$stage] ?? $processor->getDefaultWeight($stage);
|
||||
if ($processor->isHidden()) {
|
||||
$form['processors'][$processor_id]['weights'][$stage] = [
|
||||
'#type' => 'value',
|
||||
|
@ -234,7 +234,7 @@ class FacetsSummarySettingsForm extends EntityForm {
|
||||
// Clear Drupal cache for blocks to reflect recent changes.
|
||||
$this->blockManager->clearCachedDefinitions();
|
||||
$facet_source_id = $form_state->getValue('facet_source_id');
|
||||
list($type,) = explode(':', $facet_source_id);
|
||||
[$type] = explode(':', $facet_source_id);
|
||||
if ($type !== 'search_api') {
|
||||
return $facets_summary;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class FacetsSummaryBlockDeriver implements ContainerDeriverInterface {
|
||||
*/
|
||||
public function getDerivativeDefinition($derivative_id, $base_plugin_definition) {
|
||||
$derivatives = $this->getDerivativeDefinitions($base_plugin_definition);
|
||||
return isset($derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL;
|
||||
return $derivatives[$derivative_id] ?? NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,9 +51,7 @@ class ResetFacetsProcessor extends ProcessorPluginBase implements BuildProcessor
|
||||
}
|
||||
|
||||
$request_stack = \Drupal::requestStack();
|
||||
// Support 9.3+.
|
||||
// @todo remove switch after 9.3 or greater is required.
|
||||
$request = version_compare(\Drupal::VERSION, '9.3', '>=') ? $request_stack->getMainRequest() : $request_stack->getMasterRequest();
|
||||
$request = $request_stack->getMainRequest();
|
||||
$query_params = $request->query->all();
|
||||
|
||||
// Bypass all active facets and remove them from the query parameters array.
|
||||
|
@ -66,7 +66,7 @@ class ProcessorPluginBase extends PluginBase implements ProcessorInterface {
|
||||
*/
|
||||
public function getDescription() {
|
||||
$plugin_definition = $this->getPluginDefinition();
|
||||
return isset($plugin_definition['description']) ? $plugin_definition['description'] : '';
|
||||
return $plugin_definition['description'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,6 +249,7 @@ class IntegrationTest extends FacetsTestBase {
|
||||
'id' => $id,
|
||||
'facet_source_id' => 'search_api:views_page__search_api_test_view__page_1',
|
||||
];
|
||||
|
||||
$this->drupalGet('admin/config/search/facets/add-facet-summary');
|
||||
$this->submitForm($values, 'Save');
|
||||
$this->assertSession()->pageTextContains('Caching of view Search API Test Fulltext search view has been disabled.');
|
||||
|
@ -127,15 +127,8 @@ class FacetBlockAjaxController extends ControllerBase {
|
||||
$facets_blocks = array_unique($facets_blocks);
|
||||
|
||||
$new_request = Request::create($path);
|
||||
// Support 9.3+.
|
||||
// @todo remove after 9.3 or greater is required.
|
||||
if (class_exists(DrupalRequestStack::class)) {
|
||||
$request_stack = new DrupalRequestStack();
|
||||
}
|
||||
// Legacy request stack.
|
||||
else {
|
||||
$request_stack = new SymfonyRequestStack();
|
||||
}
|
||||
$request_stack = new DrupalRequestStack();
|
||||
|
||||
$processed = $this->pathProcessor->processInbound($path, $new_request);
|
||||
$processed_request = Request::create($processed);
|
||||
|
||||
|
@ -4,6 +4,9 @@ namespace Drupal\facets\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\facets\Event\GetFacetCacheContexts;
|
||||
use Drupal\facets\Event\GetFacetCacheMaxAge;
|
||||
use Drupal\facets\Event\GetFacetCacheTags;
|
||||
use Drupal\facets\Exception\Exception;
|
||||
use Drupal\facets\Exception\InvalidProcessorException;
|
||||
use Drupal\facets\Exception\InvalidQueryTypeException;
|
||||
@ -41,6 +44,8 @@ use Drupal\facets\FacetInterface;
|
||||
* "url_alias",
|
||||
* "weight",
|
||||
* "min_count",
|
||||
* "missing",
|
||||
* "missing_label",
|
||||
* "show_only_one_result",
|
||||
* "field_identifier",
|
||||
* "facet_source_id",
|
||||
@ -56,7 +61,7 @@ use Drupal\facets\FacetInterface;
|
||||
* "only_visible_when_facet_source_is_visible",
|
||||
* "processor_configs",
|
||||
* "empty_behavior",
|
||||
* "show_title"
|
||||
* "show_title",
|
||||
* },
|
||||
* links = {
|
||||
* "collection" = "/admin/config/search/facets",
|
||||
@ -317,6 +322,24 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
*/
|
||||
protected $min_count = 1;
|
||||
|
||||
protected $cache_dependencies_calculated = FALSE;
|
||||
|
||||
/**
|
||||
* The missing parameter.
|
||||
*
|
||||
* @var bool
|
||||
* The missing parameter.
|
||||
*/
|
||||
protected $missing = FALSE;
|
||||
|
||||
/**
|
||||
* The missing parameter label.
|
||||
*
|
||||
* @var string
|
||||
* The missing parameter label.
|
||||
*/
|
||||
protected $missing_label = 'others';
|
||||
|
||||
/**
|
||||
* Returns the widget plugin manager.
|
||||
*
|
||||
@ -423,7 +446,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
* The loaded processors, keyed by processor ID.
|
||||
*/
|
||||
protected function loadProcessors() {
|
||||
if (is_array($this->processors)) {
|
||||
if (isset($this->processors) && is_array($this->processors)) {
|
||||
return $this->processors;
|
||||
}
|
||||
|
||||
@ -721,7 +744,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFacetSource() {
|
||||
if (is_null($this->facet_source_instance) && $this->facet_source_id) {
|
||||
if (!isset($this->facet_source_instance) && $this->facet_source_id) {
|
||||
/** @var \Drupal\facets\FacetSource\FacetSourcePluginManager $facet_source_plugin_manager */
|
||||
$facet_source_plugin_manager = \Drupal::service('plugin.manager.facets.facet_source');
|
||||
if (!$facet_source_plugin_manager->hasDefinition($this->facet_source_id)) {
|
||||
@ -758,13 +781,13 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
}
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('facets_facet_source');
|
||||
$source_id = str_replace(':', '__', $this->facet_source_id);
|
||||
|
||||
// Load and return the facet source config object from the storage.
|
||||
$facet_source = $storage->load($source_id);
|
||||
if ($facet_source instanceof FacetSource) {
|
||||
$this->facetSourceConfig = $facet_source;
|
||||
return $this->facetSourceConfig;
|
||||
if ($source_id = str_replace(':', '__', $this->facet_source_id ?? '')) {
|
||||
// Load and return the facet source config object from the storage.
|
||||
$facet_source = $storage->load($source_id);
|
||||
if ($facet_source instanceof FacetSource) {
|
||||
$this->facetSourceConfig = $facet_source;
|
||||
return $this->facetSourceConfig;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't have a facet source config entity yet for this facet source
|
||||
@ -821,6 +844,14 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
if (in_array($result->getRawValue(), $this->active_values)) {
|
||||
$result->setActiveState(TRUE);
|
||||
}
|
||||
elseif ($result->isMissing()) {
|
||||
foreach ($this->active_values as $active_value) {
|
||||
if (str_starts_with($active_value, '!(')) {
|
||||
$result->setActiveState(TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -849,7 +880,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
foreach ($facet_source_plugin_manager->getDefinitions() as $name => $facet_source_definition) {
|
||||
if (class_exists($facet_source_definition['class']) && empty($this->facetSourcePlugins[$name])) {
|
||||
// Create our settings for this facet source..
|
||||
$config = isset($this->facetSourcePlugins[$name]) ? $this->facetSourcePlugins[$name] : [];
|
||||
$config = $this->facetSourcePlugins[$name] ?? [];
|
||||
|
||||
/** @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
|
||||
$facet_source = $facet_source_plugin_manager->createInstance($name, $config);
|
||||
@ -881,7 +912,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
|
||||
// Filter processors by status if required. Enabled processors are those
|
||||
// which have settings in the processor_configs.
|
||||
if ($only_enabled) {
|
||||
if ($processors && $only_enabled) {
|
||||
$processors_settings = $this->getProcessorConfigs();
|
||||
$processors = array_intersect_key($processors, $processors_settings);
|
||||
}
|
||||
@ -973,8 +1004,10 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
'weights' => $processor['weights'],
|
||||
'settings' => $processor['settings'],
|
||||
];
|
||||
// Sort the processors so we won't have unnecessary changes.
|
||||
// Sort the processors, so we won't have unnecessary changes.
|
||||
ksort($this->processor_configs);
|
||||
|
||||
$this->cache_dependencies_calculated = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -983,6 +1016,8 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
public function removeProcessor($processor_id) {
|
||||
unset($this->processor_configs[$processor_id]);
|
||||
unset($this->processors[$processor_id]);
|
||||
|
||||
$this->cache_dependencies_calculated = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1027,6 +1062,34 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
return $this->min_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMissing(bool $missing) {
|
||||
$this->missing = $missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isMissing(): bool {
|
||||
return $this->missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMissingLabel(string $label) {
|
||||
$this->missing_label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMissingLabel(): string {
|
||||
return $this->missing_label;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -1062,6 +1125,10 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
parent::postSave($storage, $update);
|
||||
if (!$update) {
|
||||
self::clearBlockCache();
|
||||
// Register newly created facet within its source, for the caching.
|
||||
if (($source = $this->getFacetSource()) && $source->getCacheMaxAge() !== 0) {
|
||||
$source->registerFacet($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1091,12 +1158,69 @@ class Facet extends ConfigEntityBase implements FacetInterface {
|
||||
$container->get('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
$this->calculateCacheDependencies();
|
||||
|
||||
$eventDispatcher = \Drupal::service('event_dispatcher');
|
||||
$event = new GetFacetCacheTags(parent::getCacheTags(), $this);
|
||||
$eventDispatcher->dispatch($event);
|
||||
$this->cacheTags = $event->getCacheTags() ?? $this->cacheTags;
|
||||
|
||||
return array_values($this->cacheTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
$this->calculateCacheDependencies();
|
||||
|
||||
$eventDispatcher = \Drupal::service('event_dispatcher');
|
||||
$event = new GetFacetCacheContexts(parent::getCacheContexts(), $this);
|
||||
$eventDispatcher->dispatch($event);
|
||||
$this->cacheContexts = $event->getCacheContexts() ?? $this->cacheContexts;
|
||||
|
||||
return array_values($this->cacheContexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
$this->calculateCacheDependencies();
|
||||
|
||||
$eventDispatcher = \Drupal::service('event_dispatcher');
|
||||
$event = new GetFacetCacheMaxAge(parent::getCacheMaxAge(), $this);
|
||||
$eventDispatcher->dispatch($event);
|
||||
$this->cacheMaxAge = $event->getCacheMaxAge() ?? $this->cacheMaxAge;
|
||||
|
||||
return $this->cacheMaxAge;
|
||||
}
|
||||
|
||||
protected function calculateCacheDependencies(): void {
|
||||
if (!$this->cache_dependencies_calculated) {
|
||||
if ($facet_source = $this->getFacetSource()) {
|
||||
$this->addCacheableDependency($facet_source);
|
||||
}
|
||||
|
||||
foreach ($this->getProcessors() ?? [] as $processor) {
|
||||
$this->addCacheableDependency($processor);
|
||||
}
|
||||
|
||||
$this->cache_dependencies_calculated = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the facet lazy built data when the facet is serialized.
|
||||
*/
|
||||
public function __sleep() {
|
||||
unset($this->facet_source_instance);
|
||||
unset($this->processors);
|
||||
|
||||
return parent::__sleep();
|
||||
}
|
||||
|
||||
|
@ -25,4 +25,49 @@ final class FacetsEvents {
|
||||
*/
|
||||
public const ACTIVE_FILTERS_PARSED = ActiveFiltersParsed::class;
|
||||
|
||||
/**
|
||||
* This event allows modules to change the facet links' URL if needed.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\facets\Event\UrlCreated
|
||||
*/
|
||||
public const URL_CREATED = UrlCreated::class;
|
||||
|
||||
/**
|
||||
* This event allows modules to modify a facet after it is built.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\facets\Event\PostBuildFacet
|
||||
*/
|
||||
public const POST_BUILD_FACET = PostBuildFacet::class;
|
||||
|
||||
/**
|
||||
* This event allows modules to change the cache contexts of a facet.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\facets\Event\GetFacetCacheContexts
|
||||
*/
|
||||
public const GET_FACET_CACHE_CONTEXTS = GetFacetCacheContexts::class;
|
||||
|
||||
/**
|
||||
* This event allows modules to change the cache max age of a facet.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\facets\Event\GetFacetCacheMaxAge
|
||||
*/
|
||||
public const GET_FACET_CACHE_MAX_AGE = GetFacetCacheMaxAge::class;
|
||||
|
||||
/**
|
||||
* This event allows modules to change the cache tags of a facet.
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\facets\Event\GetFacetCacheTags
|
||||
*/
|
||||
public const GET_FACET_CACHE_TAGS = GetFacetCacheTags::class;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets\Event;
|
||||
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Implements the get cache contexts event.
|
||||
*
|
||||
* This event allows modules to change the cache contexts of a facet if needed.
|
||||
*/
|
||||
final class GetFacetCacheContexts extends Event {
|
||||
|
||||
/**
|
||||
* The cache contexts.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $cacheContexts;
|
||||
|
||||
/**
|
||||
* The facet.
|
||||
*
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
private $facet;
|
||||
|
||||
/**
|
||||
* GetCacheContexts constructor.
|
||||
*
|
||||
* @param string[] $cacheContexts
|
||||
* The cache contexts.
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*/
|
||||
public function __construct($cacheContexts, FacetInterface $facet) {
|
||||
$this->cacheContexts = $cacheContexts;
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache contexts.
|
||||
*
|
||||
* @return string[]
|
||||
* The cache contexts.
|
||||
*/
|
||||
public function getCacheContexts(): array {
|
||||
return $this->cacheContexts ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache contexts.
|
||||
*
|
||||
* @param string[] $cacheContexts
|
||||
* The cache contexts.
|
||||
*/
|
||||
public function setCacheContexts($cacheContexts): void {
|
||||
$this->cacheContexts = $cacheContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet.
|
||||
*/
|
||||
public function getFacet() {
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets\Event;
|
||||
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Implements the get cache max age event.
|
||||
*
|
||||
* This event allows modules to change the cache max age of a facet if needed.
|
||||
*/
|
||||
final class GetFacetCacheMaxAge extends Event {
|
||||
|
||||
/**
|
||||
* The cache max age.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $cacheMaxAge;
|
||||
|
||||
/**
|
||||
* The facet.
|
||||
*
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
private $facet;
|
||||
|
||||
/**
|
||||
* GetCacheMaxAge constructor.
|
||||
*
|
||||
* @param int $cacheMaxAge
|
||||
* The cache max age.
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*/
|
||||
public function __construct($cacheMaxAge, FacetInterface $facet) {
|
||||
$this->cacheMaxAge = $cacheMaxAge;
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache max age.
|
||||
*
|
||||
* @return int
|
||||
* The cache max age.
|
||||
*/
|
||||
public function getCacheMaxAge(): int {
|
||||
return $this->cacheMaxAge ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache max age.
|
||||
*
|
||||
* @param int $cacheMaxAge
|
||||
* The cache max age.
|
||||
*/
|
||||
public function setCacheMaxAge($cacheMaxAge): void {
|
||||
$this->cacheMaxAge = $cacheMaxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet.
|
||||
*/
|
||||
public function getFacet() {
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets\Event;
|
||||
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Implements the get cache tags event.
|
||||
*
|
||||
* This event allows modules to change the cache tags of a facet if needed.
|
||||
*/
|
||||
final class GetFacetCacheTags extends Event {
|
||||
|
||||
/**
|
||||
* The cache tags.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $cacheTags;
|
||||
|
||||
/**
|
||||
* The facet.
|
||||
*
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
private $facet;
|
||||
|
||||
/**
|
||||
* GetCacheTags constructor.
|
||||
*
|
||||
* @param string[] $cacheTags
|
||||
* The cache tags.
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*/
|
||||
public function __construct($cacheTags, FacetInterface $facet) {
|
||||
$this->cacheTags = $cacheTags;
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache tags.
|
||||
*
|
||||
* @return string[]
|
||||
* The cache tags.
|
||||
*/
|
||||
public function getCacheTags(): array {
|
||||
return $this->cacheTags ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache tags.
|
||||
*
|
||||
* @param string[] $cacheTags
|
||||
* The cache tags.
|
||||
*/
|
||||
public function setCacheTags($cacheTags): void {
|
||||
$this->cacheTags = $cacheTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet.
|
||||
*/
|
||||
public function getFacet() {
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets\Event;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Implements the PostBuildFacet event.
|
||||
*
|
||||
* This event allows modules to modify a facet after it is built and before it
|
||||
* will be cached and rendered.
|
||||
*/
|
||||
final class PostBuildFacet extends Event {
|
||||
|
||||
/**
|
||||
* The facet.
|
||||
*
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
private $facet;
|
||||
|
||||
/**
|
||||
* PreAddFacetSourceCacheableDependencies constructor.
|
||||
*
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*/
|
||||
public function __construct(FacetInterface $facet) {
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet.
|
||||
*/
|
||||
public function getFacet(): FacetInterface {
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the facet.
|
||||
*
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFacet(FacetInterface $facet): void {
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets\Event;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Result\ResultInterface;
|
||||
use Drupal\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Implements the url created event.
|
||||
*
|
||||
* This event allows modules to change the facet link's URL if needed.
|
||||
*/
|
||||
final class UrlCreated extends Event {
|
||||
|
||||
/**
|
||||
* The get parameters.
|
||||
*
|
||||
* @var \Drupal\Core\Url
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* The facet result.
|
||||
*
|
||||
* @var \Drupal\facets\Result\ResultInterface
|
||||
*/
|
||||
private $facetResult;
|
||||
|
||||
/**
|
||||
* The facet.
|
||||
*
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
private $facet;
|
||||
|
||||
/**
|
||||
* UrlCreated constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The facet link URL.
|
||||
* @param \Drupal\facets\Result\ResultInterface $facetResult
|
||||
* The facet result.
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet.
|
||||
*/
|
||||
public function __construct(Url $url, ResultInterface $facetResult, FacetInterface $facet) {
|
||||
$this->url = $url;
|
||||
$this->facetResult = $facetResult;
|
||||
$this->facet = $facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL.
|
||||
*/
|
||||
public function getUrl(): Url {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL to set.
|
||||
*/
|
||||
public function setUrl(Url $url): void {
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet result.
|
||||
*
|
||||
* Only to be used as context, because changing this will not result in any
|
||||
* changes to the final url.
|
||||
*
|
||||
* @return \Drupal\facets\Result\ResultInterface
|
||||
* The facet result.
|
||||
*/
|
||||
public function getFacetResult() {
|
||||
return $this->facetResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facet.
|
||||
*
|
||||
* Only to be used as context, because changing this will not result in any
|
||||
* changes to the final url.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet.
|
||||
*/
|
||||
public function getFacet() {
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
}
|
@ -504,4 +504,36 @@ interface FacetInterface extends ConfigEntityInterface {
|
||||
*/
|
||||
public function getMinCount();
|
||||
|
||||
/**
|
||||
* Sets the missing parameter.
|
||||
*
|
||||
* @param bool $missing
|
||||
* Whether to show a missing item or not.
|
||||
*/
|
||||
public function setMissing(bool $missing);
|
||||
|
||||
/**
|
||||
* Returns the missing parameter.
|
||||
*
|
||||
* @return bool
|
||||
* Minimum count.
|
||||
*/
|
||||
public function isMissing(): bool;
|
||||
|
||||
/**
|
||||
* Sets the missing parameter label.
|
||||
*
|
||||
* @param string $label
|
||||
* The label.
|
||||
*/
|
||||
public function setMissingLabel(string $label);
|
||||
|
||||
/**
|
||||
* Returns the missing parameter label.
|
||||
*
|
||||
* @return string
|
||||
* The label.
|
||||
*/
|
||||
public function getMissingLabel(): string;
|
||||
|
||||
}
|
||||
|
@ -191,6 +191,7 @@ class FacetListBuilder extends DraggableListBuilder {
|
||||
'#type' => 'markup',
|
||||
'#markup' => 'Facet source',
|
||||
],
|
||||
'machine_name' => ['#markup' => $facet_source['id']],
|
||||
'title' => [
|
||||
'#theme_wrappers' => [
|
||||
'container' => [
|
||||
@ -198,9 +199,9 @@ class FacetListBuilder extends DraggableListBuilder {
|
||||
],
|
||||
],
|
||||
'#type' => 'markup',
|
||||
'#markup' => $facet_source['id'],
|
||||
'#markup' => $facet_source['label'],
|
||||
'#wrapper_attributes' => [
|
||||
'colspan' => 3,
|
||||
'colspan' => 2,
|
||||
],
|
||||
],
|
||||
'operations' => [
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
namespace Drupal\facets\FacetManager;
|
||||
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\facets\Event\PostBuildFacet;
|
||||
use Drupal\facets\Exception\InvalidProcessorException;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\FacetSource\FacetSourcePluginManager;
|
||||
@ -109,11 +111,13 @@ class DefaultFacetManager {
|
||||
* The facet source ID to process.
|
||||
*/
|
||||
public function alterQuery(&$query, $facetsource_id) {
|
||||
$query_is_cacheable = $query instanceof RefinableCacheableDependencyInterface;
|
||||
/** @var \Drupal\facets\FacetInterface[] $facets */
|
||||
$facets = $this->getFacetsByFacetSourceId($facetsource_id);
|
||||
foreach ($facets as $facet) {
|
||||
|
||||
foreach ($facets as $facet) {
|
||||
$processors = $facet->getProcessors();
|
||||
|
||||
if (isset($processors['dependent_processor'])) {
|
||||
$conditions = $processors['dependent_processor']->getConfiguration();
|
||||
|
||||
@ -126,7 +130,6 @@ class DefaultFacetManager {
|
||||
}
|
||||
|
||||
foreach ($enabled_conditions as $facet_id => $condition_settings) {
|
||||
|
||||
if (!isset($facets[$facet_id]) || !$processors['dependent_processor']->isConditionMet($condition_settings, $facets[$facet_id])) {
|
||||
// The conditions are not met anymore, remove the active items.
|
||||
$facet->setActiveItems([]);
|
||||
@ -139,7 +142,12 @@ class DefaultFacetManager {
|
||||
unset($active_filters[$facet->id()]);
|
||||
$urlProcessor->setActiveFilters($active_filters);
|
||||
}
|
||||
|
||||
// Add "dependend facet" cacheabillity to make sure that whenever
|
||||
// its preferences will change, for instance to "negate", query
|
||||
// results cache will take it to consideration.
|
||||
if ($query_is_cacheable) {
|
||||
$query->addCacheableDependency($facet);
|
||||
}
|
||||
// Don't convert this facet's active items into query conditions.
|
||||
// Continue with the next facet.
|
||||
continue(2);
|
||||
@ -156,6 +164,10 @@ class DefaultFacetManager {
|
||||
]
|
||||
);
|
||||
$query_type_plugin->execute();
|
||||
// Merge cache medata that gathered from facet and its processors.
|
||||
if ($query_is_cacheable) {
|
||||
$query->addCacheableDependency($facet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,6 +248,7 @@ class DefaultFacetManager {
|
||||
}
|
||||
$post_query_processor->postQuery($facet);
|
||||
}
|
||||
|
||||
$this->processedFacets[$facetsource_id][$facet->id()] = $facet;
|
||||
}
|
||||
}
|
||||
@ -326,7 +339,11 @@ class DefaultFacetManager {
|
||||
|
||||
$facet->setResults($results);
|
||||
|
||||
$this->builtFacets[$facet->getFacetSourceId()][$facet->id()] = $facet;
|
||||
$eventDispatcher = \Drupal::service('event_dispatcher');
|
||||
$event = new PostBuildFacet($facet);
|
||||
$eventDispatcher->dispatch($event);
|
||||
|
||||
$this->builtFacets[$facet->getFacetSourceId()][$facet->id()] = $event->getFacet();
|
||||
}
|
||||
|
||||
return $this->builtFacets[$facet->getFacetSourceId()][$facet->id()];
|
||||
@ -338,7 +355,8 @@ class DefaultFacetManager {
|
||||
* This method delegates to the relevant plugins to render a facet, it calls
|
||||
* out to a widget plugin to do the actual rendering when results are found.
|
||||
* When no results are found it calls out to the correct empty result plugin
|
||||
* to build a render array.
|
||||
* to build a render array. Renderable array will include all facet plugins
|
||||
* cache metadata that were used to build this facet.
|
||||
*
|
||||
* Before doing any rendering, the processors that implement the
|
||||
* BuildProcessorInterface enabled on this facet will run.
|
||||
@ -369,7 +387,6 @@ class DefaultFacetManager {
|
||||
/** @var \Drupal\facets\Widget\WidgetPluginInterface $widget */
|
||||
$widget = $facet->getWidgetInstance();
|
||||
$build = $widget->build($facet);
|
||||
|
||||
// No results behavior handling. Return a custom text or false depending on
|
||||
// settings.
|
||||
if (empty($facet->getResults())) {
|
||||
@ -439,7 +456,7 @@ class DefaultFacetManager {
|
||||
* call returnBuiltFacet() instead.
|
||||
*
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* The facet to process.
|
||||
* The facet to process with a collected plugins cache metadata.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface|null
|
||||
* The updated facet if it exists, NULL otherwise.
|
||||
@ -456,7 +473,7 @@ class DefaultFacetManager {
|
||||
* The facet to process.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The built facet.
|
||||
* The built Facet object with a collected plugins cache metadata.
|
||||
*/
|
||||
public function returnBuiltFacet(FacetInterface $facet) {
|
||||
return $this->processBuild($facet);
|
||||
|
@ -87,7 +87,7 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
|
||||
*/
|
||||
public function getDerivativeDefinition($derivative_id, $base_plugin_definition) {
|
||||
$derivatives = $this->getDerivativeDefinitions($base_plugin_definition);
|
||||
return isset($derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL;
|
||||
return $derivatives[$derivative_id] ?? NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\FacetSource;
|
||||
|
||||
use Drupal\Core\Cache\UncacheableDependencyTrait;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
@ -12,6 +13,11 @@ use Drupal\facets\QueryType\QueryTypePluginManager;
|
||||
/**
|
||||
* Defines a base class from which other facet sources may extend.
|
||||
*
|
||||
* By default all plugins that will extend this class will disable facets
|
||||
* caching mechanism. It is strongly recommended to turn it on by implementing
|
||||
* own methods for the CacheableDependencyInterface interface and
|
||||
* ::registerFacet() method.
|
||||
*
|
||||
* Plugins extending this class need to define a plugin definition array through
|
||||
* annotation. The definition includes the following keys:
|
||||
* - id: The unique, system-wide identifier of the facet source.
|
||||
@ -24,6 +30,7 @@ use Drupal\facets\QueryType\QueryTypePluginManager;
|
||||
* @see plugin_api
|
||||
*/
|
||||
abstract class FacetSourcePluginBase extends PluginBase implements FacetSourcePluginInterface, ContainerFactoryPluginInterface {
|
||||
use UncacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The plugin manager.
|
||||
@ -153,4 +160,10 @@ abstract class FacetSourcePluginBase extends PluginBase implements FacetSourcePl
|
||||
$this->facet->setFieldIdentifier($field_identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerFacet(FacetInterface $facet) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Drupal\facets\FacetSource;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
|
||||
@ -15,7 +16,7 @@ use Drupal\facets\FacetInterface;
|
||||
*
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface FacetSourcePluginInterface extends PluginFormInterface, DependentPluginInterface {
|
||||
interface FacetSourcePluginInterface extends PluginFormInterface, DependentPluginInterface, CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Fills the facet entities with results from the facet source.
|
||||
@ -115,4 +116,16 @@ interface FacetSourcePluginInterface extends PluginFormInterface, DependentPlugi
|
||||
*/
|
||||
public function buildFacet();
|
||||
|
||||
/**
|
||||
* Register newly added facet within its source.
|
||||
*
|
||||
* Add facet cache tags and contexts into the facet source, to make sure that
|
||||
* search results will change whenever facets will be updated. Usually can be
|
||||
* achieved by adding facet entity as a cache dependency to a search results.
|
||||
*
|
||||
* @param \Drupal\facets\FacetInterface $facet
|
||||
* Facet entity that being inserted.
|
||||
*/
|
||||
public function registerFacet(FacetInterface $facet);
|
||||
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ class FacetForm extends EntityForm {
|
||||
$form['facet_settings']['empty_behavior_container']['empty_behavior_text'] = [
|
||||
'#type' => 'text_format',
|
||||
'#title' => $this->t('Empty text'),
|
||||
'#format' => isset($empty_behavior_config['text_format']) ? $empty_behavior_config['text_format'] : 'plain_text',
|
||||
'#format' => $empty_behavior_config['text_format'] ?? 'plain_text',
|
||||
'#editor' => TRUE,
|
||||
'#default_value' => isset($empty_behavior_config['text_format']) ? $empty_behavior_config['text'] : '',
|
||||
];
|
||||
@ -465,35 +465,34 @@ class FacetForm extends EntityForm {
|
||||
'#title' => $this->t('Use hierarchy'),
|
||||
'#default_value' => $facet->getUseHierarchy(),
|
||||
];
|
||||
if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
|
||||
$form['facet_settings']['use_hierarchy']['#disabled'] = TRUE;
|
||||
$form['facet_settings']['use_hierarchy']['#description'] = $this->t('This setting only works with Search API based facets.');
|
||||
}
|
||||
else {
|
||||
if ($facet->getFacetSource() instanceof SearchApiDisplay) {
|
||||
$processor_url = Url::fromRoute('entity.search_api_index.processors', [
|
||||
'search_api_index' => $facet->getFacetSource()->getIndex()->id(),
|
||||
]);
|
||||
$description = $this->t('Renders the items using hierarchy. Make sure to enable the hierarchy processor on the <a href=":processor-url">Search api index processor configuration</a> for this to work as expected. If disabled all items will be flattened.', [
|
||||
$description = $this->t('Renders the items using hierarchy. Depending on the selected plugin below, make sure to enable the hierarchy processor on the <a href=":processor-url">Search api index processor configuration</a> for this to work as expected. If disabled all items might be flattened.', [
|
||||
':processor-url' => $processor_url->toString(),
|
||||
]);
|
||||
$form['facet_settings']['use_hierarchy']['#description'] = $description;
|
||||
|
||||
$hierarchy = $facet->getHierarchy();
|
||||
$options = array_map(function (HierarchyPluginBase $plugin) {
|
||||
return $plugin->getPluginDefinition()['label'];
|
||||
}, $facet->getHierarchies());
|
||||
$form['facet_settings']['hierarchy'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Hierarchy type'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $hierarchy ? $hierarchy['type'] : '',
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="facet_settings[use_hierarchy]"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
else {
|
||||
$description = $this->t('Renders the items using hierarchy. Note that some of the selectable plugins below will not supports all search backends. The taxonomy plugin will only work with Search API.');
|
||||
}
|
||||
$form['facet_settings']['use_hierarchy']['#description'] = $description;
|
||||
|
||||
$hierarchy = $facet->getHierarchy();
|
||||
$options = array_map(function (HierarchyPluginBase $plugin) {
|
||||
return $plugin->getPluginDefinition()['label'];
|
||||
}, $facet->getHierarchies());
|
||||
$form['facet_settings']['hierarchy'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Hierarchy type'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $hierarchy ? $hierarchy['type'] : '',
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="facet_settings[use_hierarchy]"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$form['facet_settings']['keep_hierarchy_parents_active'] = [
|
||||
'#type' => 'checkbox',
|
||||
@ -535,8 +534,9 @@ class FacetForm extends EntityForm {
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Minimum count'),
|
||||
'#default_value' => $facet->getMinCount(),
|
||||
'#description' => $this->t('Only display the results if there is this minimum amount of results.'),
|
||||
'#description' => $this->t('Only display the results if there is this minimum amount of results. The default is "1". A setting "0" might result in a list of all possible facet items, regardless of the actual search query. But the result of a minimum count of "0" is not reliable and may very on the type of the field, the Search API backend and even between different releases or runtime configurations of the backend (for example Solr). Therefore it is highly recommended to avoid any feature that depends on a minimum count of "0".'),
|
||||
'#maxlength' => 4,
|
||||
'#min' => 0,
|
||||
'#required' => TRUE,
|
||||
];
|
||||
if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
|
||||
@ -545,6 +545,30 @@ class FacetForm extends EntityForm {
|
||||
$form['facet_settings']['min_count']['#description'] .= $this->t('This setting only works with Search API based facets.');
|
||||
}
|
||||
|
||||
$form['facet_settings']['missing'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show missing'),
|
||||
'#default_value' => $facet->isMissing(),
|
||||
'#description' => $this->t('Add a facet item that counts and selects all search results which match the current query but do not belong to any of the facet items.'),
|
||||
];
|
||||
if (!$facet->getFacetSource() instanceof SearchApiDisplay) {
|
||||
$form['facet_settings']['missing']['#disabled'] = TRUE;
|
||||
$form['facet_settings']['missing']['#description'] .= '<br />';
|
||||
$form['facet_settings']['missing']['#description'] .= $this->t('This setting only works with Search API based facets.');
|
||||
}
|
||||
|
||||
$form['facet_settings']['missing_label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label of missing items'),
|
||||
'#description' => $this->t('Label of the facet item for which do not belong to any of the regular items.'),
|
||||
'#default_value' => $facet->getMissingLabel(),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="facet_settings[missing]"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$form['facet_settings']['weight'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Weight'),
|
||||
@ -597,9 +621,7 @@ class FacetForm extends EntityForm {
|
||||
foreach ($processors_by_stage as $stage => $processors) {
|
||||
/** @var \Drupal\facets\Processor\ProcessorInterface $processor */
|
||||
foreach ($processors as $processor_id => $processor) {
|
||||
$weight = isset($processor_settings[$processor_id]['weights'][$stage])
|
||||
? $processor_settings[$processor_id]['weights'][$stage]
|
||||
: $processor->getDefaultWeight($stage);
|
||||
$weight = $processor_settings[$processor_id]['weights'][$stage] ?? $processor->getDefaultWeight($stage);
|
||||
if ($processor->isHidden()) {
|
||||
$form['processors'][$processor_id]['weights'][$stage] = [
|
||||
'#type' => 'value',
|
||||
@ -682,10 +704,10 @@ class FacetForm extends EntityForm {
|
||||
|
||||
// Validate url alias.
|
||||
$url_alias = $form_state->getValue(['facet_settings', 'url_alias']);
|
||||
if ($url_alias == 'page') {
|
||||
if ($url_alias === 'page') {
|
||||
$form_state->setErrorByName('url_alias', $this->t('This URL alias is not allowed.'));
|
||||
}
|
||||
elseif (preg_match('/[^a-zA-Z0-9_~\.\-]/', $url_alias)) {
|
||||
elseif (preg_match('/[^a-zA-Z0-9_~.\-]/', $url_alias)) {
|
||||
$form_state->setErrorByName('url_alias', $this->t('The URL alias contains characters that are not allowed.'));
|
||||
}
|
||||
}
|
||||
@ -733,6 +755,18 @@ class FacetForm extends EntityForm {
|
||||
'min_count',
|
||||
]
|
||||
));
|
||||
$facet->setMissing((bool) $form_state->getValue(
|
||||
[
|
||||
'facet_settings',
|
||||
'missing',
|
||||
]
|
||||
));
|
||||
$facet->setMissingLabel($form_state->getValue(
|
||||
[
|
||||
'facet_settings',
|
||||
'missing_label',
|
||||
]
|
||||
));
|
||||
$facet->setOnlyVisibleWhenFacetSourceIsVisible($form_state->getValue(
|
||||
[
|
||||
'facet_settings',
|
||||
|
@ -292,7 +292,7 @@ class FacetSettingsForm extends EntityForm {
|
||||
$this->messenger()->addMessage($this->t('Facet %name has been updated.', ['%name' => $facet->getName()]));
|
||||
}
|
||||
|
||||
list($type,) = explode(':', $facet_source_id);
|
||||
[$type] = explode(':', $facet_source_id);
|
||||
if ($type !== 'search_api') {
|
||||
return $facet;
|
||||
}
|
||||
@ -305,9 +305,10 @@ class FacetSettingsForm extends EntityForm {
|
||||
if ($view->display_handler instanceof Block) {
|
||||
$facet->setOnlyVisibleWhenFacetSourceIsVisible(FALSE);
|
||||
}
|
||||
$view->display_handler->overrideOption('cache', ['type' => 'none']);
|
||||
$view->save();
|
||||
$this->messenger()->addMessage($this->t('Caching of view %view has been disabled.', ['%view' => $view->storage->label()]));
|
||||
$views_cache_type = $view->display_handler->getOption('cache')['type'];
|
||||
if ($views_cache_type !== 'none') {
|
||||
$this->messenger()->addMessage($this->t('You may experience issues, because %view use cache. In case you will try to turn set cache plugin to none.', ['%view' => $view->storage->label()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,12 +102,12 @@ class FacetSourceEditForm extends EntityForm {
|
||||
$form['breadcrumb']['active'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Append active facets to breadcrumb'),
|
||||
'#default_value' => isset($breadcrumb_settings['active']) ? $breadcrumb_settings['active'] : FALSE,
|
||||
'#default_value' => $breadcrumb_settings['active'] ?? FALSE,
|
||||
];
|
||||
$form['breadcrumb']['before'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show facet label before active facet'),
|
||||
'#default_value' => isset($breadcrumb_settings['before']) ? $breadcrumb_settings['before'] : TRUE,
|
||||
'#default_value' => $breadcrumb_settings['before'] ?? TRUE,
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="breadcrumb[active]"]' => ['checked' => TRUE],
|
||||
@ -117,7 +117,7 @@ class FacetSourceEditForm extends EntityForm {
|
||||
$form['breadcrumb']['group'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Group active items under same crumb (not implemented yet - now always grouping)'),
|
||||
'#default_value' => isset($breadcrumb_settings['group']) ? $breadcrumb_settings['group'] : FALSE,
|
||||
'#default_value' => $breadcrumb_settings['group'] ?? FALSE,
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="breadcrumb[active]"]' => ['checked' => TRUE],
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\facets\Hierarchy;
|
||||
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\UncacheableDependencyTrait;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -9,17 +11,21 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A base class for plugins that implements most of the boilerplate.
|
||||
*
|
||||
* By default all plugins that will extend this class will disable facets
|
||||
* caching mechanism. It is strongly recommended to turn it on by implementing
|
||||
* own methods for the CacheableDependencyInterface interface.
|
||||
*/
|
||||
abstract class HierarchyPluginBase extends ProcessorPluginBase implements HierarchyInterface, ContainerFactoryPluginInterface {
|
||||
abstract class HierarchyPluginBase extends ProcessorPluginBase implements HierarchyInterface, ContainerFactoryPluginInterface, CacheableDependencyInterface {
|
||||
|
||||
use UncacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
$request_stack = $container->get('request_stack');
|
||||
// Support 9.3+.
|
||||
// @todo remove switch after 9.3 or greater is required.
|
||||
$request = version_compare(\Drupal::VERSION, '9.3', '>=') ? $request_stack->getMainRequest() : $request_stack->getMasterRequest();
|
||||
$request = $request_stack->getMainRequest();
|
||||
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $request);
|
||||
}
|
||||
|
@ -2,10 +2,14 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\FacetManager\DefaultFacetManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
@ -33,6 +37,11 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
*/
|
||||
protected $facetStorage;
|
||||
|
||||
/**
|
||||
* @var \Drupal\facets\FacetInterface
|
||||
*/
|
||||
protected $facet;
|
||||
|
||||
/**
|
||||
* Construct a FacetBlock instance.
|
||||
*
|
||||
@ -70,24 +79,19 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
/** @var \Drupal\facets\FacetInterface $facet */
|
||||
$facet = $this->facetStorage->load($this->getDerivativeId());
|
||||
|
||||
// No need to build the facet if it does not need to be visible.
|
||||
if ($facet->getOnlyVisibleWhenFacetSourceIsVisible() &&
|
||||
(!$facet->getFacetSource() || !$facet->getFacetSource()->isRenderedInCurrentRequest())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Do not build the facet if the block is being previewed.
|
||||
if ($this->getContextValue('in_preview')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$facet = $this->getFacet();
|
||||
|
||||
// Let the facet_manager build the facets.
|
||||
$build = $this->facetManager->build($facet);
|
||||
|
||||
if (!empty($build)) {
|
||||
CacheableMetadata::createFromObject($facet)->applyTo($build);
|
||||
|
||||
// Add extra elements from facet source, for example, ajax scripts.
|
||||
// @see Drupal\facets\Plugin\facets\facet_source\SearchApiDisplay
|
||||
/** @var \Drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source */
|
||||
@ -122,47 +126,45 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facet block entity.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* The facet entity.
|
||||
*/
|
||||
protected function getFacet(): FacetInterface {
|
||||
if (!$this->facet) {
|
||||
$this->facet = $this->facetStorage->load($this->getDerivativeId());
|
||||
}
|
||||
return $this->facet;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return $this->getFacet()->getCacheTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return $this->getFacet()->getCacheContexts();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// A facet block cannot be cached, because it must always match the current
|
||||
// search results, and Search API gets those search results from a data
|
||||
// source that can be external to Drupal. Therefore it is impossible to
|
||||
// guarantee that the search results are in sync with the data managed by
|
||||
// Drupal. Consequently, it is not possible to cache the search results at
|
||||
// all. If the search results cannot be cached, then neither can the facets,
|
||||
// because they must always match.
|
||||
// Fortunately, facet blocks are rendered using a lazy builder (like all
|
||||
// blocks in Drupal), which means their rendering can be deferred (unlike
|
||||
// the search results, which are the main content of the page, and deferring
|
||||
// their rendering would mean sending an empty page to the user). This means
|
||||
// that facet blocks can be rendered and sent *after* the initial page was
|
||||
// loaded, by installing the BigPipe (big_pipe) module.
|
||||
//
|
||||
// When BigPipe is enabled, the search results will appear first, and then
|
||||
// each facet block will appear one-by-one, in DOM order.
|
||||
// See https://www.drupal.org/project/big_pipe.
|
||||
//
|
||||
// In a future version of Facet API, this could be refined, but due to the
|
||||
// reliance on external data sources, it will be very difficult if not
|
||||
// impossible to improve this significantly.
|
||||
//
|
||||
// Note: when using Drupal core's Search module instead of the contributed
|
||||
// Search API module, the above limitations do not apply, but for now it is
|
||||
// not considered worth the effort to optimize this just for Drupal core's
|
||||
// Search.
|
||||
return 0;
|
||||
return $this->getFacet()->getCacheMaxAge();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
/** @var \Drupal\facets\FacetInterface $facet */
|
||||
$facet = $this->facetStorage->load($this->getDerivativeId());
|
||||
|
||||
return ['config' => [$facet->getConfigDependencyName()]];
|
||||
return ['config' => [$this->getFacet()->getConfigDependencyName()]];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,4 +187,19 @@ class FacetBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
return $this->t('Placeholder for the "@facet" facet', ['@facet' => $this->getDerivativeId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Allow to render facet block if one of the following conditions are met:
|
||||
* - facet is allowed to be displayed regardless of the source visibility
|
||||
* - facet source is rendered in the same request as facet.
|
||||
*/
|
||||
public function blockAccess(AccountInterface $account) {
|
||||
$facet = $this->getFacet();
|
||||
return AccessResult::allowedIf(
|
||||
!$facet->getOnlyVisibleWhenFacetSourceIsVisible()
|
||||
|| ($facet->getFacetSource() && $facet->getFacetSource()->isRenderedInCurrentRequest())
|
||||
)->addCacheableDependency($facet);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class FacetBlockDeriver implements ContainerDeriverInterface {
|
||||
*/
|
||||
public function getDerivativeDefinition($derivative_id, $base_plugin_definition) {
|
||||
$derivatives = $this->getDerivativeDefinitions($base_plugin_definition);
|
||||
return isset($derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL;
|
||||
return $derivatives[$derivative_id] ?? NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,8 @@
|
||||
namespace Drupal\facets\Plugin\facets\facet_source;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Extension\ModuleHandler;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
@ -24,6 +26,13 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
/**
|
||||
* Provides a facet source based on a Search API display.
|
||||
*
|
||||
* @todo The support for non views displays might be removed from facets 3.x and
|
||||
* moved into a sub or contributed module. So this class needs to become
|
||||
* something like "SearchApiViewsDisplay" and a "SearchApiCustomDisplay"
|
||||
* plugin needs to be provided by the sub or contributed module. At the
|
||||
* moment we have switches within this class for example to get the cache
|
||||
* metadata. Those need to be removed.
|
||||
*
|
||||
* @FacetsFacetSource(
|
||||
* id = "search_api",
|
||||
* deriver = "Drupal\facets\Plugin\facets\facet_source\SearchApiDisplayDeriver"
|
||||
@ -31,6 +40,14 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*/
|
||||
class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSourceInterface {
|
||||
|
||||
/**
|
||||
* List of Search API cache plugins that works with Facets cache system.
|
||||
*/
|
||||
const CACHEABLE_PLUGINS = [
|
||||
'search_api_tag',
|
||||
'search_api_time',
|
||||
];
|
||||
|
||||
/**
|
||||
* The search index the query should is executed on.
|
||||
*
|
||||
@ -115,9 +132,7 @@ class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSo
|
||||
$container->get('plugin.manager.facets.query_type'),
|
||||
$container->get('search_api.query_helper'),
|
||||
$container->get('plugin.manager.search_api.display'),
|
||||
// Support 9.3+.
|
||||
// @todo remove switch after 9.3 or greater is required.
|
||||
version_compare(\Drupal::VERSION, '9.3', '>=') ? $request_stack->getMainRequest() : $request_stack->getMasterRequest(),
|
||||
$request_stack->getMainRequest(),
|
||||
$container->get('module_handler')
|
||||
);
|
||||
}
|
||||
@ -159,11 +174,12 @@ class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSo
|
||||
|
||||
// If there are no results, we can check the Search API Display plugin has
|
||||
// configuration for views. If that configuration exists, we can execute
|
||||
// that view and try to use it's results.
|
||||
// that view and try to use its results.
|
||||
$display_definition = $this->getDisplay()->getPluginDefinition();
|
||||
if ($results === NULL && isset($display_definition['view_id'])) {
|
||||
$view = Views::getView($display_definition['view_id']);
|
||||
$view->setDisplay($display_definition['view_display']);
|
||||
$view->preExecute();
|
||||
$view->execute();
|
||||
$results = $this->searchApiQueryHelper->getResults($search_id);
|
||||
}
|
||||
@ -187,7 +203,7 @@ class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSo
|
||||
$configuration = [
|
||||
'query' => $results->getQuery(),
|
||||
'facet' => $facet,
|
||||
'results' => isset($facet_results[$facet->getFieldIdentifier()]) ? $facet_results[$facet->getFieldIdentifier()] : [],
|
||||
'results' => $facet_results[$facet->getFieldIdentifier()] ?? [],
|
||||
];
|
||||
|
||||
// Get the Facet Specific Query Type so we can process the results
|
||||
@ -418,4 +434,94 @@ class SearchApiDisplay extends FacetSourcePluginBase implements SearchApiFacetSo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
if ($views_display = $this->getViewsDisplay()) {
|
||||
return $views_display
|
||||
->getDisplay()
|
||||
->getCacheMetadata()
|
||||
->getCacheContexts();
|
||||
}
|
||||
|
||||
// Custom display implementations should provide their own cache metadata.
|
||||
$display = $this->getDisplay();
|
||||
if ($display instanceof CacheableDependencyInterface) {
|
||||
return $display->getCacheContexts();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
if ($views_display = $this->getViewsDisplay()) {
|
||||
return Cache::mergeTags(
|
||||
$views_display->getDisplay()->getCacheMetadata()->getCacheTags(),
|
||||
$views_display->getCacheTags()
|
||||
);
|
||||
}
|
||||
|
||||
// Custom display implementations should provide their own cache metadata.
|
||||
$display = $this->getDisplay();
|
||||
if ($display instanceof CacheableDependencyInterface) {
|
||||
return $display->getCacheTags();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
if ($views_display = $this->getViewsDisplay()) {
|
||||
$cache_plugin = $views_display->getDisplay()->getPlugin('cache');
|
||||
return Cache::mergeMaxAges(
|
||||
$views_display->getDisplay()->getCacheMetadata()->getCacheMaxAge(),
|
||||
$cache_plugin ? $cache_plugin->getCacheMaxAge() : 0
|
||||
);
|
||||
}
|
||||
|
||||
// Custom display implementations should provide their own cache metadata.
|
||||
$display = $this->getDisplay();
|
||||
if ($display instanceof CacheableDependencyInterface) {
|
||||
return $display->getCacheMaxAge();
|
||||
}
|
||||
|
||||
// Caching is not supported.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Alter views view cache metadata:
|
||||
* - When view being re-saved it will collect all cache metadata from its
|
||||
* plugins, including cache plugin.
|
||||
* - Search API cache plugin will pre-execute the query and collect cacheable
|
||||
* metadata from all facets and will pass it to the view.
|
||||
*
|
||||
* View will use collected cache tags to invalidate search results. And cache
|
||||
* context provided by the facet to vary results.
|
||||
*
|
||||
* @see \Drupal\views\Plugin\views\display\DisplayPluginBase::calculateCacheMetadata()
|
||||
* @see \Drupal\search_api\Plugin\views\cache\SearchApiCachePluginTrait::alterCacheMetadata()
|
||||
* @see \Drupal\facets\FacetManager\DefaultFacetManager::alterQuery()
|
||||
*/
|
||||
public function registerFacet(FacetInterface $facet) {
|
||||
if (
|
||||
// On the config-sync or site install view will already have all required
|
||||
// cache tags, so don't react if it's already there.
|
||||
!in_array('config:' . $facet->getConfigDependencyName(), $this->getCacheTags())
|
||||
// Re-save it only if we know that views cache plugin works with facets.
|
||||
&& in_array($this->getViewsDisplay()->getDisplay()->getOption('cache')['type'], static::CACHEABLE_PLUGINS)
|
||||
) {
|
||||
$this->getViewsDisplay()->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\hierarchy;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\Hierarchy\HierarchyPluginBase;
|
||||
|
||||
/**
|
||||
@ -15,6 +16,8 @@ use Drupal\facets\Hierarchy\HierarchyPluginBase;
|
||||
*/
|
||||
class DateItems extends HierarchyPluginBase {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* Static cache for the parents.
|
||||
*
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\hierarchy;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\facets\Hierarchy\HierarchyPluginBase;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
@ -96,7 +98,7 @@ class Taxonomy extends HierarchyPluginBase {
|
||||
$current_tid = $parent;
|
||||
$parents[$id][] = $parent;
|
||||
}
|
||||
return isset($parents[$id]) ? $parents[$id] : [];
|
||||
return $parents[$id] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,6 +154,17 @@ class Taxonomy extends HierarchyPluginBase {
|
||||
if (!$topLevelTerms) {
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = $this->getTermStorage()->load($id);
|
||||
|
||||
// Issue #3260603:
|
||||
// Due to a bug in core
|
||||
// https://www.drupal.org/project/drupal/issues/2723323
|
||||
// it may happen that a taxonomy term is still referenced in a field,
|
||||
// even though the term has been deleted.
|
||||
// Not checking the term is empty produces a fatal error.
|
||||
if (!$term instanceof TermInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$topLevelTerms = array_map(function ($term) {
|
||||
return $term->tid;
|
||||
}, $this->getTermStorage()->loadTree($term->bundle(), 0, 1));
|
||||
@ -197,4 +210,11 @@ class Taxonomy extends HierarchyPluginBase {
|
||||
return $this->termParents[$tid] = reset($parents)->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['taxonomy_term:list']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\Processor\SortProcessorPluginBase;
|
||||
use Drupal\facets\Processor\SortProcessorInterface;
|
||||
use Drupal\facets\Result\Result;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Result\Result;
|
||||
*/
|
||||
class ActiveWidgetOrderProcessor extends SortProcessorPluginBase implements SortProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
@ -23,6 +24,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class BooleanItemProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -25,6 +26,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
*/
|
||||
class CombineFacetProcessor extends ProcessorPluginBase implements BuildProcessorInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
@ -148,7 +151,6 @@ class CombineFacetProcessor extends ProcessorPluginBase implements BuildProcesso
|
||||
/** @var \Drupal\facets\Entity\Facet $current_facet */
|
||||
$current_facet = $this->facetStorage->load($facet_id);
|
||||
$current_facet = $this->facetsManager->returnBuiltFacet($current_facet);
|
||||
|
||||
switch ($settings['mode']) {
|
||||
case 'union':
|
||||
$results = $keyed_results + $current_facet->getResultsKeyedByRawValue();
|
||||
@ -162,6 +164,8 @@ class CombineFacetProcessor extends ProcessorPluginBase implements BuildProcesso
|
||||
$results = array_intersect_key($keyed_results, $current_facet->getResultsKeyedByRawValue());
|
||||
break;
|
||||
}
|
||||
// Pass build processor information into current facet.
|
||||
$facet->addCacheableDependency($current_facet);
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class CountLimitProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\Processor\SortProcessorPluginBase;
|
||||
use Drupal\facets\Processor\SortProcessorInterface;
|
||||
use Drupal\facets\Result\Result;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Result\Result;
|
||||
*/
|
||||
class CountWidgetOrderProcessor extends SortProcessorPluginBase implements SortProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -22,6 +23,8 @@ use Drupal\facets\Plugin\facets\query_type\SearchApiDate;
|
||||
*/
|
||||
class DateItemProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -25,6 +26,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
*/
|
||||
class DependentFacetProcessor extends ProcessorPluginBase implements BuildProcessorInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
@ -125,6 +128,7 @@ class DependentFacetProcessor extends ProcessorPluginBase implements BuildProces
|
||||
'#title' => $this->t('Values'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => empty($config[$facet->id()]['values']) ? '' : $config[$facet->id()]['values'],
|
||||
'#description' => $this->t('Enter a comma-separated list of values. Example: value1, value2, value3'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="facet_settings[' . $this->getPluginId() . '][settings][' . $facet->id() . '][enable]"]' => ['checked' => TRUE],
|
||||
@ -168,10 +172,10 @@ class DependentFacetProcessor extends ProcessorPluginBase implements BuildProces
|
||||
}
|
||||
|
||||
foreach ($enabled_conditions as $facet_id => $condition_settings) {
|
||||
|
||||
/** @var \Drupal\facets\Entity\Facet $current_facet */
|
||||
$current_facet = $this->facetStorage->load($facet_id);
|
||||
$current_facet = $this->facetsManager->returnBuiltFacet($current_facet);
|
||||
$facet->addCacheableDependency($current_facet);
|
||||
|
||||
if (!$this->isConditionMet($condition_settings, $current_facet)) {
|
||||
return [];
|
||||
@ -202,7 +206,7 @@ class DependentFacetProcessor extends ProcessorPluginBase implements BuildProces
|
||||
if ($condition_settings['condition'] === 'values') {
|
||||
$return = FALSE;
|
||||
|
||||
$values = explode(',', $condition_settings['values']);
|
||||
$values = array_map('trim', explode(',', $condition_settings['values']));
|
||||
foreach ($facet->getActiveItems() as $value) {
|
||||
if (in_array($value, $values)) {
|
||||
$return = TRUE;
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Component\Transliteration\TransliterationInterface;
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\facets\Processor\SortProcessorInterface;
|
||||
use Drupal\facets\Processor\SortProcessorPluginBase;
|
||||
@ -24,6 +25,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
*/
|
||||
class DisplayValueWidgetOrderProcessor extends SortProcessorPluginBase implements SortProcessorInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The transliteration service.
|
||||
*
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class ExcludeSpecifiedItemsProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class GranularItemProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class HideActiveItemsProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class HideInactiveSiblingsProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -29,6 +32,7 @@ class HideInactiveSiblingsProcessor extends ProcessorPluginBase implements Build
|
||||
|
||||
if ($facet->getUseHierarchy()) {
|
||||
$hierarchy = $facet->getHierarchyInstance();
|
||||
$facet->addCacheableDependency($hierarchy);
|
||||
|
||||
if (!$facet->getKeepHierarchyParentsActive()) {
|
||||
$parents_of_active_items = [];
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class HideNonNarrowingResultProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class HideOnlyOneItemProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -21,6 +22,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class HierarchyProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* An array of all entity ids in the active resultset which are a child.
|
||||
*
|
||||
@ -38,8 +41,10 @@ class HierarchyProcessor extends ProcessorPluginBase implements BuildProcessorIn
|
||||
foreach ($results as $result) {
|
||||
$keyed_results[$result->getRawValue()] = $result;
|
||||
}
|
||||
$hierarchy = $facet->getHierarchyInstance();
|
||||
$facet->addCacheableDependency($hierarchy);
|
||||
|
||||
$parent_groups = $facet->getHierarchyInstance()->getChildIds(array_keys($keyed_results));
|
||||
$parent_groups = $hierarchy->getChildIds(array_keys($keyed_results));
|
||||
$keyed_results = $this->buildHierarchicalTree($keyed_results, $parent_groups);
|
||||
|
||||
// Remove children from primary level.
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
@ -30,6 +31,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
*/
|
||||
class ListItemProcessor extends ProcessorPluginBase implements BuildProcessorInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The config manager.
|
||||
*
|
||||
@ -133,12 +136,13 @@ class ListItemProcessor extends ProcessorPluginBase implements BuildProcessorInt
|
||||
$fieldDefinition->getTargetEntityTypeId(),
|
||||
$fieldDefinition->getName()
|
||||
);
|
||||
|
||||
if ($fieldDefinition instanceof BaseFieldDefinition) {
|
||||
if (isset($base_fields[$field->getPropertyPath()])) {
|
||||
$field = $base_fields[$field->getPropertyPath()];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Diggs down to get the referenced field the entity reference is based
|
||||
// on.
|
||||
elseif ($this->configManager->loadConfigEntityByName($referenced_entity_name) !== NULL) {
|
||||
@ -148,6 +152,7 @@ class ListItemProcessor extends ProcessorPluginBase implements BuildProcessorInt
|
||||
}
|
||||
if ($field instanceof FieldStorageDefinitionInterface) {
|
||||
if ($field->getName() !== 'type') {
|
||||
$facet->addCacheableDependency($field);
|
||||
$allowed_values = options_allowed_values($field);
|
||||
if (!empty($allowed_values)) {
|
||||
return $this->overWriteDisplayValues($results, $allowed_values);
|
||||
@ -158,6 +163,7 @@ class ListItemProcessor extends ProcessorPluginBase implements BuildProcessorInt
|
||||
// bundle field.
|
||||
$list_bundles = $this->entityTypeBundleInfo->getBundleInfo($entity);
|
||||
if (!empty($list_bundles)) {
|
||||
$facet->addCacheTags(['entity_bundles']);
|
||||
foreach ($list_bundles as $key => $bundle) {
|
||||
$allowed_values[$key] = $bundle['label'];
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\Processor\SortProcessorPluginBase;
|
||||
use Drupal\facets\Processor\SortProcessorInterface;
|
||||
use Drupal\facets\Result\Result;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Result\Result;
|
||||
*/
|
||||
class RawValueWidgetOrderProcessor extends SortProcessorPluginBase implements SortProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
@ -20,6 +21,8 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
*/
|
||||
class ShowOnlyDeepestLevelItemsProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -22,6 +23,8 @@ use Drupal\facets\Result\Result;
|
||||
*/
|
||||
class ShowSiblingsProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -31,7 +34,9 @@ class ShowSiblingsProcessor extends ProcessorPluginBase implements BuildProcesso
|
||||
$rawValues = array_map(function ($result) {
|
||||
return $result->getRawValue();
|
||||
}, $results);
|
||||
foreach ($facet->getHierarchyInstance()->getSiblingIds($rawValues, $facet->getActiveItems(), $this->getConfiguration()['show_parent_siblings']) as $siblingId) {
|
||||
$hierarchy = $facet->getHierarchyInstance();
|
||||
$facet->addCacheableDependency($hierarchy);
|
||||
foreach ($hierarchy->getSiblingIds($rawValues, $facet->getActiveItems(), $this->getConfiguration()['show_parent_siblings']) as $siblingId) {
|
||||
$results[] = new Result($facet, $siblingId, $siblingId, 0);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
|
||||
@ -25,6 +27,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
*/
|
||||
class TermWeightWidgetOrderProcessor extends SortProcessorPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
@ -126,4 +130,11 @@ class TermWeightWidgetOrderProcessor extends SortProcessorPluginBase implements
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['taxonomy_term_list']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
@ -167,7 +168,7 @@ class TranslateEntityAggregatedFieldProcessor extends ProcessorPluginBase implem
|
||||
if ($entity instanceof TranslatableInterface && $entity->hasTranslation($language_interface->getId())) {
|
||||
$entity = $entity->getTranslation($language_interface->getId());
|
||||
}
|
||||
|
||||
$facet->addCacheableDependency($entity);
|
||||
// Overwrite the result's display value.
|
||||
$results[$i]->setDisplayValue($entity->label());
|
||||
}
|
||||
@ -179,6 +180,7 @@ class TranslateEntityAggregatedFieldProcessor extends ProcessorPluginBase implem
|
||||
// bundle field.
|
||||
foreach ($entity_type_ids as $entity) {
|
||||
$list_bundles = $this->entityTypeBundleInfo->getBundleInfo($entity);
|
||||
$facet->addCacheTags(['entity_bundles']);
|
||||
if (!empty($list_bundles)) {
|
||||
foreach ($list_bundles as $key => $bundle) {
|
||||
$allowed_values[$key] = $bundle['label'];
|
||||
@ -231,4 +233,21 @@ class TranslateEntityAggregatedFieldProcessor extends ProcessorPluginBase implem
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), ['languages:language_interface']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// This will work unless the Search API Query uses "wrong" caching. Ideally
|
||||
// we would set a cache tag to invalidate the cache whenever a translatable
|
||||
// entity is added or changed. But there's no tag in drupal yet.
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
@ -116,7 +117,12 @@ class TranslateEntityProcessor extends ProcessorPluginBase implements BuildProce
|
||||
// Loop over all results.
|
||||
foreach ($results as $i => $result) {
|
||||
if (!isset($entities[$ids[$i]])) {
|
||||
unset($results[$i]);
|
||||
if ($result->isMissing()) {
|
||||
$results[$i]->setDisplayValue($facet->getMissingLabel());
|
||||
}
|
||||
else {
|
||||
unset($results[$i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -128,7 +134,7 @@ class TranslateEntityProcessor extends ProcessorPluginBase implements BuildProce
|
||||
if ($entity instanceof TranslatableInterface && $entity->hasTranslation($language_interface->getId())) {
|
||||
$entity = $entity->getTranslation($language_interface->getId());
|
||||
}
|
||||
|
||||
$facet->addCacheableDependency($entity);
|
||||
// Overwrite the result's display value.
|
||||
$results[$i]->setDisplayValue($entity->label());
|
||||
}
|
||||
@ -158,4 +164,21 @@ class TranslateEntityProcessor extends ProcessorPluginBase implements BuildProce
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), ['languages:language_interface']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// This will work unless the Search API Query uses "wrong" caching. Ideally
|
||||
// we would set a cache tag to invalidate the cache whenever a translatable
|
||||
// entity is added or changed. But there's no tag in drupal yet.
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
@ -34,6 +35,7 @@ class UidToUserNameCallbackProcessor extends ProcessorPluginBase implements Buil
|
||||
/** @var \Drupal\user\Entity\User $user */
|
||||
if (($user = User::load($result->getRawValue())) !== NULL) {
|
||||
$result->setDisplayValue($user->getDisplayName());
|
||||
$facet->addCacheableDependency($user);
|
||||
$usernames[] = $result;
|
||||
}
|
||||
}
|
||||
@ -68,4 +70,18 @@ class UidToUserNameCallbackProcessor extends ProcessorPluginBase implements Buil
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['user_list']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\facets\Exception\InvalidProcessorException;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
@ -12,7 +13,7 @@ use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
* The URL processor handler triggers the actual url processor.
|
||||
*
|
||||
* The URL processor handler allows managing the weight of the actual URL
|
||||
* processor per Facet. This handler will trigger the actual.
|
||||
* processor per Facet. This handler will trigger the actual.
|
||||
*
|
||||
* @FacetsUrlProcessor, which can be configured on the Facet source.
|
||||
*
|
||||
@ -82,4 +83,25 @@ class UrlProcessorHandler extends ProcessorPluginBase implements BuildProcessorI
|
||||
$this->processor->setActiveItems($facet);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return CacheableMetadata::createFromObject($this->processor)->getCacheTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return CacheableMetadata::createFromObject($this->processor)->getCacheContexts();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return CacheableMetadata::createFromObject($this->processor)->getCacheMaxAge();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class SearchApiGranular extends QueryTypeRangeBase {
|
||||
$query_operator = $this->facet->getQueryOperator();
|
||||
$facet_results = [];
|
||||
foreach ($this->results as $result) {
|
||||
if ($result['count'] || $query_operator == 'or') {
|
||||
if ($result['count'] || $query_operator === 'or') {
|
||||
$result_filter = trim($result['filter'], '"');
|
||||
$facet_results[] = new Result($this->facet, $result_filter, $result_filter, $result['count']);
|
||||
}
|
||||
|
@ -58,12 +58,17 @@ class SearchApiRange extends QueryTypePluginBase {
|
||||
if (!empty($this->results)) {
|
||||
$facet_results = [];
|
||||
foreach ($this->results as $result) {
|
||||
if ($result['count'] || $query_operator == 'or') {
|
||||
if ($result['count'] || $query_operator === 'or') {
|
||||
$count = $result['count'];
|
||||
while (is_array($result['filter'])) {
|
||||
$result['filter'] = current($result['filter']);
|
||||
}
|
||||
$result_filter = trim($result['filter'], '"');
|
||||
if ($result_filter === 'NULL' || $result_filter === '') {
|
||||
// "Missing" facet items could not be handled in ranges.
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = new Result($this->facet, $result_filter, $result_filter, $count);
|
||||
$facet_results[] = $result;
|
||||
}
|
||||
|
@ -47,7 +47,17 @@ class SearchApiString extends QueryTypePluginBase {
|
||||
if (count($active_items)) {
|
||||
$filter = $query->createConditionGroup($operator, ['facet:' . $field_identifier]);
|
||||
foreach ($active_items as $value) {
|
||||
$filter->addCondition($this->facet->getFieldIdentifier(), $value, $exclude ? '<>' : '=');
|
||||
if (str_starts_with($value, '!(')) {
|
||||
/** @var \Drupal\facets\UrlProcessor\UrlProcessorInterface $urlProcessor */
|
||||
$urlProcessor = $this->facet->getProcessors()['url_processor_handler']->getProcessor();
|
||||
foreach (explode($urlProcessor->getDelimiter(), substr($value, 2, -1)) as $missing_value) {
|
||||
// Note that $exclude needs to be inverted for "missing".
|
||||
$filter->addCondition($this->facet->getFieldIdentifier(), $missing_value, !$exclude ? '<>' : '=');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$filter->addCondition($this->facet->getFieldIdentifier(), $value, $exclude ? '<>' : '=');
|
||||
}
|
||||
}
|
||||
$query->addConditionGroup($filter);
|
||||
}
|
||||
@ -63,8 +73,8 @@ class SearchApiString extends QueryTypePluginBase {
|
||||
if (!empty($this->results)) {
|
||||
$facet_results = [];
|
||||
foreach ($this->results as $result) {
|
||||
if ($result['count'] || $query_operator == 'or') {
|
||||
$result_filter = $result['filter'];
|
||||
if ($result['count'] || $query_operator === 'or') {
|
||||
$result_filter = $result['filter'] ?? '';
|
||||
if ($result_filter[0] === '"') {
|
||||
$result_filter = substr($result_filter, 1);
|
||||
}
|
||||
@ -73,10 +83,16 @@ class SearchApiString extends QueryTypePluginBase {
|
||||
}
|
||||
$count = $result['count'];
|
||||
$result = new Result($this->facet, $result_filter, $result_filter, $count);
|
||||
$facet_results[] = $result;
|
||||
$result->setMissing($this->facet->isMissing() && $result_filter === '!');
|
||||
$facet_results[$result_filter] = $result;
|
||||
}
|
||||
}
|
||||
$this->facet->setResults($facet_results);
|
||||
|
||||
if (isset($facet_results['!']) && $facet_results['!']->isMissing()) {
|
||||
$facet_results['!']->setMissingFilters(array_keys($facet_results));
|
||||
}
|
||||
|
||||
$this->facet->setResults(array_values($facet_results));
|
||||
}
|
||||
|
||||
return $this->facet;
|
||||
|
@ -2,17 +2,18 @@
|
||||
|
||||
namespace Drupal\facets\Plugin\facets\url_processor;
|
||||
|
||||
use Drupal\Core\Cache\UnchangingCacheableDependencyTrait;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\facets\Event\ActiveFiltersParsed;
|
||||
use Drupal\facets\Event\QueryStringCreated;
|
||||
use Drupal\facets\Event\UrlCreated;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\UrlProcessor\UrlProcessorPluginBase;
|
||||
use Drupal\facets\Utility\FacetsUrlGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* Query string URL processor.
|
||||
@ -25,6 +26,8 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
*/
|
||||
class QueryString extends UrlProcessorPluginBase {
|
||||
|
||||
use UnchangingCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* A string of how to represent the facet in the url.
|
||||
*
|
||||
@ -39,12 +42,20 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* The URL generator.
|
||||
*
|
||||
* @var \Drupal\facets\Utility\FacetsUrlGenerator
|
||||
*/
|
||||
protected $urlGenerator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher) {
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher, FacetsUrlGenerator $urlGenerator) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager);
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->initializeActiveFilters();
|
||||
}
|
||||
|
||||
@ -58,7 +69,8 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
$plugin_definition,
|
||||
$container->get('request_stack')->getCurrentRequest(),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('event_dispatcher')
|
||||
$container->get('event_dispatcher'),
|
||||
$container->get('facets.utility.url_generator')
|
||||
);
|
||||
}
|
||||
|
||||
@ -84,6 +96,8 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
// Set the url alias from the facet object.
|
||||
$this->urlAlias = $facet->getUrlAlias();
|
||||
|
||||
// In case of a view page display, the facet source has a path, If the
|
||||
// source is a block, the path is null.
|
||||
$facet_source_path = $facet->getFacetSource()->getPath();
|
||||
$request = $this->getRequestByFacetSourcePath($facet_source_path);
|
||||
$requestUrl = $this->getUrlForRequest($facet_source_path, $request);
|
||||
@ -108,9 +122,14 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
$this->buildUrls($facet, $children);
|
||||
}
|
||||
|
||||
$filter_missing = '';
|
||||
if ($result->getRawValue() === NULL) {
|
||||
$filter_string = NULL;
|
||||
}
|
||||
elseif ($result->isMissing()) {
|
||||
$filter_missing = $this->urlAlias . $this->getSeparator() . '!(';
|
||||
$filter_string = $filter_missing . implode($this->getDelimiter(), $result->getMissingFilters()) . ')';
|
||||
}
|
||||
else {
|
||||
$filter_string = $this->urlAlias . $this->getSeparator() . $result->getRawValue();
|
||||
}
|
||||
@ -121,11 +140,11 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
// If the value is active, remove the filter string from the parameters.
|
||||
if ($result->isActive()) {
|
||||
foreach ($filter_params as $key => $filter_param) {
|
||||
if ($filter_param == $filter_string) {
|
||||
if ($filter_param === $filter_string || ($filter_missing && str_starts_with($filter_param, $filter_missing))) {
|
||||
unset($filter_params[$key]);
|
||||
}
|
||||
}
|
||||
if ($facet->getUseHierarchy()) {
|
||||
if ($facet->getUseHierarchy() && !$result->isMissing()) {
|
||||
$id = $result->getRawValue();
|
||||
|
||||
// Disable child filters.
|
||||
@ -197,7 +216,7 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
}
|
||||
}
|
||||
|
||||
// Allow other modules to alter the result url built.
|
||||
// Allow other modules to alter the result url query string built.
|
||||
$event = new QueryStringCreated($result_get_params, $filter_params, $result, $this->activeFilters, $facet);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
$filter_params = $event->getFilterParameters();
|
||||
@ -216,14 +235,18 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
// See https://www.drupal.org/node/2898189.
|
||||
unset($new_url_params['page']);
|
||||
|
||||
// Remove core wrapper format (e.g. render-as-ajax-response) paremeters.
|
||||
// Remove core wrapper format (e.g. render-as-ajax-response) parameters.
|
||||
unset($new_url_params[MainContentViewSubscriber::WRAPPER_FORMAT]);
|
||||
|
||||
// Set the new url parameters.
|
||||
$url->setOption('query', $new_url_params);
|
||||
}
|
||||
|
||||
$result->setUrl($url);
|
||||
// Allow other modules to alter the result url built.
|
||||
$event = new UrlCreated($url, $result, $facet);
|
||||
$this->eventDispatcher->dispatch($event);
|
||||
|
||||
$result->setUrl($event->getUrl());
|
||||
}
|
||||
|
||||
// Restore page parameter again. See https://www.drupal.org/node/2726455.
|
||||
@ -266,9 +289,8 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
/**
|
||||
* Gets the URL object for a request.
|
||||
*
|
||||
* This method statically caches the URL object for a request based on the
|
||||
* facet source path. This reduces subsequent calls to the processor from
|
||||
* having to regenerate the URL object.
|
||||
* This method delegates to the URL generator service. But we keep it for
|
||||
* backward compatibility for custom implementations that extend this class.
|
||||
*
|
||||
* @param string $facet_source_path
|
||||
* The facet source path.
|
||||
@ -279,37 +301,7 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
* The URL.
|
||||
*/
|
||||
protected function getUrlForRequest($facet_source_path, Request $request) {
|
||||
/** @var \Drupal\Core\Url[] $requestUrlsByPath */
|
||||
$requestUrlsByPath = &drupal_static(__CLASS__ . __FUNCTION__, []);
|
||||
|
||||
if (array_key_exists($facet_source_path, $requestUrlsByPath)) {
|
||||
return $requestUrlsByPath[$facet_source_path];
|
||||
}
|
||||
|
||||
// Try to grab any route params from the original request.
|
||||
// In case of request path not having a matching route, Url generator will
|
||||
// fail with.
|
||||
try {
|
||||
$requestUrl = Url::createFromRequest($request);
|
||||
}
|
||||
catch (ResourceNotFoundException $e) {
|
||||
// Bypass exception if no path available.
|
||||
// Should be unreachable in default FacetSource implementations,
|
||||
// but you never know.
|
||||
if (!$facet_source_path) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$requestUrl = Url::fromUserInput($facet_source_path, [
|
||||
'query' => [
|
||||
'_format' => $this->request->get('_format'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$requestUrl->setOption('attributes', ['rel' => 'nofollow']);
|
||||
$requestUrlsByPath[$facet_source_path] = $requestUrl;
|
||||
return $requestUrl;
|
||||
return $this->urlGenerator->getUrlForRequest($request, $facet_source_path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -417,4 +409,11 @@ class QueryString extends UrlProcessorPluginBase {
|
||||
return $mapping[$facet_source_id][$facet_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return ['url.query_args'];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,14 +67,14 @@ class LinksWidget extends WidgetPluginBase {
|
||||
|
||||
unset($active_filters[$facet->id()]);
|
||||
|
||||
// Only if there are still active filters, use url generator.
|
||||
$urlGenerator = \Drupal::service('facets.utility.url_generator');
|
||||
if ($active_filters) {
|
||||
$url = \Drupal::service('facets.utility.url_generator')
|
||||
->getUrl($active_filters, FALSE);
|
||||
$url = $urlGenerator->getUrl($active_filters, FALSE);
|
||||
}
|
||||
else {
|
||||
$request = \Drupal::request();
|
||||
$url = Url::createFromRequest($request);
|
||||
$facet_source = $facet->getFacetSource();
|
||||
$url = $urlGenerator->getUrlForRequest($request, $facet_source ? $facet_source->getPath() : NULL);
|
||||
$params = $request->query->all();
|
||||
unset($params[$url_processor->getFilterKey()]);
|
||||
if (\array_key_exists('page', $params)) {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\facets\Processor;
|
||||
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\UncacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Entity\DependencyTrait;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
@ -9,9 +11,14 @@ use Drupal\facets\FacetInterface;
|
||||
|
||||
/**
|
||||
* A base class for plugins that implements most of the boilerplate.
|
||||
*
|
||||
* By default all plugins that will extend this class will disable facets
|
||||
* caching mechanism. It is strongly recommended to turn it on by implementing
|
||||
* own methods for the CacheableDependencyInterface interface.
|
||||
*/
|
||||
class ProcessorPluginBase extends PluginBase implements ProcessorInterface {
|
||||
class ProcessorPluginBase extends PluginBase implements ProcessorInterface, CacheableDependencyInterface {
|
||||
|
||||
use UncacheableDependencyTrait;
|
||||
use DependencyTrait;
|
||||
|
||||
/**
|
||||
@ -69,7 +76,7 @@ class ProcessorPluginBase extends PluginBase implements ProcessorInterface {
|
||||
*/
|
||||
public function getDescription() {
|
||||
$plugin_definition = $this->getPluginDefinition();
|
||||
return isset($plugin_definition['description']) ? $plugin_definition['description'] : '';
|
||||
return $plugin_definition['description'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,20 +2,21 @@
|
||||
|
||||
namespace Drupal\facets\Processor;
|
||||
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
|
||||
/**
|
||||
* A base class for plugins that implements some boilerplate for a widget order.
|
||||
*/
|
||||
abstract class SortProcessorPluginBase extends ProcessorPluginBase implements SortProcessorInterface {
|
||||
abstract class SortProcessorPluginBase extends ProcessorPluginBase implements SortProcessorInterface, CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet) {
|
||||
$processors = $facet->getProcessors();
|
||||
$config = isset($processors[$this->getPluginId()]) ? $processors[$this->getPluginId()] : NULL;
|
||||
$config = $processors[$this->getPluginId()] ?? NULL;
|
||||
|
||||
$build['sort'] = [
|
||||
'#type' => 'radios',
|
||||
|
@ -93,7 +93,7 @@ abstract class QueryTypePluginBase extends PluginBase implements QueryTypeInterf
|
||||
'limit' => $this->facet->getHardLimit(),
|
||||
'operator' => $this->facet->getQueryOperator(),
|
||||
'min_count' => $this->facet->getMinCount(),
|
||||
'missing' => FALSE,
|
||||
'missing' => $this->facet->isMissing(),
|
||||
'query_type' => $this->getPluginId(),
|
||||
];
|
||||
}
|
||||
|
@ -71,9 +71,14 @@ abstract class QueryTypeRangeBase extends QueryTypePluginBase {
|
||||
foreach ($this->results as $result) {
|
||||
// Go through the results and add facet results grouped by filters
|
||||
// defined by self::calculateResultFilter().
|
||||
if ($result['count'] || $query_operator == 'or') {
|
||||
if ($result['count'] || $query_operator === 'or') {
|
||||
$count = $result['count'];
|
||||
if ($result_filter = $this->calculateResultFilter(trim($result['filter'], '"'))) {
|
||||
if ($result_filter === 'NULL' || $result_filter === '') {
|
||||
// "Missing" facet items could not be handled in ranges.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($facet_results[$result_filter['raw']])) {
|
||||
$facet_results[$result_filter['raw']]->setCount(
|
||||
$facet_results[$result_filter['raw']]->getCount() + $count
|
||||
|
@ -38,6 +38,20 @@ class Result implements ResultInterface {
|
||||
*/
|
||||
protected $count = 0;
|
||||
|
||||
/**
|
||||
* Indicates if this is the additional result item for "missing".
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $missing = FALSE;
|
||||
|
||||
/**
|
||||
* Other filters that might become active if result item isn't "missing".
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $missingFilters = [];
|
||||
|
||||
/**
|
||||
* The Url object.
|
||||
*
|
||||
@ -106,6 +120,36 @@ class Result implements ResultInterface {
|
||||
$this->count = (int) $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isMissing(): bool {
|
||||
return $this->missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMissing(bool $missing) {
|
||||
$this->missing = $missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMissingFilters(): array {
|
||||
return $this->missingFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMissingFilters(array $filters) {
|
||||
$this->missingFilters = array_filter($filters, static function ($filter) {
|
||||
return $filter !== '!';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -49,6 +49,38 @@ interface ResultInterface {
|
||||
*/
|
||||
public function setCount($count);
|
||||
|
||||
/**
|
||||
* Set if this result represents the "missing" facet item.
|
||||
*
|
||||
* @return bool
|
||||
* True if this result represents the missing facet item.
|
||||
*/
|
||||
public function isMissing(): bool;
|
||||
|
||||
/**
|
||||
* Returns true if this result represents the "missing" facet item.
|
||||
*
|
||||
* @param bool $missing
|
||||
* True if this result represents the missing facet item.
|
||||
*/
|
||||
public function setMissing(bool $missing);
|
||||
|
||||
/**
|
||||
* Get the filter values of the non-missing values to be inverted.
|
||||
*
|
||||
* @return array
|
||||
* The filter values of the non-missing values to be inverted.
|
||||
*/
|
||||
public function getMissingFilters(): array;
|
||||
|
||||
/**
|
||||
* Set the filter values of the non-missing values to be inverted.
|
||||
*
|
||||
* @param array $filters
|
||||
* The filter values of the non-missing values to be inverted.
|
||||
*/
|
||||
public function setMissingFilters(array $filters);
|
||||
|
||||
/**
|
||||
* Returns the url.
|
||||
*
|
||||
|
@ -59,6 +59,14 @@ interface UrlProcessorInterface {
|
||||
*/
|
||||
public function getSeparator();
|
||||
|
||||
/**
|
||||
* Returns the multi-value delimiter.
|
||||
*
|
||||
* @return string
|
||||
* A string containing the multi-value delimiter.
|
||||
*/
|
||||
public function getDelimiter(): string;
|
||||
|
||||
/**
|
||||
* Returns the active filters.
|
||||
*
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\facets\UrlProcessor;
|
||||
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\UncacheableDependencyTrait;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\facets\Exception\InvalidProcessorException;
|
||||
@ -12,8 +14,14 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* A base class for plugins that implements most of the boilerplate.
|
||||
*
|
||||
* By default all plugins that will extend this class will disable facets
|
||||
* caching mechanism. It is strongly recommended to turn it on by implementing
|
||||
* own methods for the CacheableDependencyInterface interface.
|
||||
*/
|
||||
abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements UrlProcessorInterface, ContainerFactoryPluginInterface {
|
||||
abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements UrlProcessorInterface, ContainerFactoryPluginInterface, CacheableDependencyInterface {
|
||||
|
||||
use UncacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The query string variable.
|
||||
@ -27,10 +35,18 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url
|
||||
* The url separator variable.
|
||||
*
|
||||
* @var string
|
||||
* The sepatator to use between field and value.
|
||||
* The separator to use between field and value.
|
||||
*/
|
||||
protected $separator;
|
||||
|
||||
/**
|
||||
* The delimiter for multiple values.
|
||||
*
|
||||
* @var string
|
||||
* The delimiter to use between multiple values.
|
||||
*/
|
||||
protected $delimiter = '|';
|
||||
|
||||
/**
|
||||
* The clone of the current request object.
|
||||
*
|
||||
@ -68,6 +84,13 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDelimiter(): string {
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class.
|
||||
*
|
||||
@ -119,9 +142,7 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
// Support 9.3+.
|
||||
// @todo remove switch after 9.3 or greater is required.
|
||||
version_compare(\Drupal::VERSION, '9.3', '>=') ? $request_stack->getMainRequest() : $request_stack->getMasterRequest(),
|
||||
$request_stack->getMainRequest(),
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
@ -150,8 +150,8 @@ class FacetsDateHandler {
|
||||
|
||||
// Gets gap numbers for both the gap and minimum gap, checks if the next gap
|
||||
// is within the limit set by the $min_gap parameter.
|
||||
$gap_num = isset($gap_numbers[$gap]) ? $gap_numbers[$gap] : 6;
|
||||
$min_num = isset($gap_numbers[$min_gap]) ? $gap_numbers[$min_gap] : 1;
|
||||
$gap_num = $gap_numbers[$gap] ?? 6;
|
||||
$min_num = $gap_numbers[$min_gap] ?? 1;
|
||||
return ($gap_num > $min_num) ? array_search($gap_num - 1, $gap_numbers) : $min_gap;
|
||||
}
|
||||
|
||||
@ -383,8 +383,8 @@ class FacetsDateHandler {
|
||||
static::FACETS_DATE_SECOND => 1,
|
||||
];
|
||||
|
||||
$gap1_num = isset($gap_numbers[$gap1]) ? $gap_numbers[$gap1] : 6;
|
||||
$gap2_num = isset($gap_numbers[$gap2]) ? $gap_numbers[$gap2] : 6;
|
||||
$gap1_num = $gap_numbers[$gap1] ?? 6;
|
||||
$gap2_num = $gap_numbers[$gap2] ?? 6;
|
||||
|
||||
if ($gap1_num == $gap2_num) {
|
||||
return 0;
|
||||
|
@ -3,8 +3,11 @@
|
||||
namespace Drupal\facets\Utility;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\facets\Result\Result;
|
||||
use Drupal\facets\UrlProcessor\UrlProcessorPluginManager;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* Facets Url Generator service.
|
||||
@ -109,4 +112,69 @@ class FacetsUrlGenerator {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL object for a request.
|
||||
*
|
||||
* This method statically caches the URL object for a request based on the
|
||||
* facet source path. This reduces subsequent calls to the processor from
|
||||
* having to regenerate the URL object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
* @param string $facet_source_path
|
||||
* The facet source path.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL.
|
||||
*/
|
||||
public function getUrlForRequest(Request $request, $facet_source_path = NULL): Url {
|
||||
/** @var \Drupal\Core\Url[] $requestUrlsByPath */
|
||||
$requestUrlsByPath = &drupal_static(__CLASS__ . __FUNCTION__, []);
|
||||
$request_uri = $request->getRequestUri();
|
||||
|
||||
if (array_key_exists($request_uri, $requestUrlsByPath)) {
|
||||
return $requestUrlsByPath[$request_uri];
|
||||
}
|
||||
|
||||
// Try to grab any route params from the original request.
|
||||
// In case of request path not having a matching route, Url generator will
|
||||
// fail with.
|
||||
try {
|
||||
$requestUrl = Url::createFromRequest($request);
|
||||
}
|
||||
catch (ResourceNotFoundException $e) {
|
||||
// Bypass exception if no path available.
|
||||
// Should be unreachable in default FacetSource implementations,
|
||||
// but you never know.
|
||||
if ($facet_source_path) {
|
||||
$requestUrl = Url::fromUserInput($facet_source_path, [
|
||||
'query' => [
|
||||
'_format' => \Drupal::request()->get('_format'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
else {
|
||||
if ('system.404' === $request->attributes->get('_route')) {
|
||||
// It seems that a facet that is configured to be rendered without its
|
||||
// facet source is currently rendered on a dedicated "page not found"
|
||||
// page. If the facet source has a valid path we would not land here
|
||||
// but in the condition above. So the facet source must be view block
|
||||
// display or something similar. In this case we could assume that
|
||||
// such a facet takes care about its link target itself and doesn't
|
||||
// depend on the current path or the facet source path. Let's provide
|
||||
// the front page as valid fallback to let the facet do its job.
|
||||
$requestUrl = Url::fromRoute('<front>');
|
||||
}
|
||||
else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$requestUrl->setOption('attributes', ['rel' => 'nofollow']);
|
||||
$requestUrlsByPath[$request_uri] = $requestUrl;
|
||||
|
||||
return $requestUrl;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -81,12 +81,6 @@ abstract class WidgetPluginBase extends PluginBase implements WidgetPluginInterf
|
||||
'class' => [$facet->getActiveItems() ? 'facet-active' : 'facet-inactive'],
|
||||
],
|
||||
'#context' => !empty($widget['type']) ? ['list_style' => $widget['type']] : [],
|
||||
'#cache' => [
|
||||
'contexts' => [
|
||||
'url.path',
|
||||
'url.query_args',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,20 @@
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{% if cache_hash %}
|
||||
<!-- facets cacheable metadata
|
||||
hash: {{ cache_hash }}
|
||||
{% if cache_contexts %}
|
||||
contexts: {{ cache_contexts }}
|
||||
{%- endif %}
|
||||
{% if cache_tags %}
|
||||
tags: {{ cache_tags }}
|
||||
{%- endif %}
|
||||
{% if cache_max_age %}
|
||||
max age: {{ cache_max_age }}
|
||||
{%- endif %}
|
||||
-->
|
||||
{%- endif %}
|
||||
<div class="facets-widget- {{- facet.widget.type -}} ">
|
||||
{% if facet.widget.type %}
|
||||
{%- set attributes = attributes.addClass('item-list__' ~ facet.widget.type) %}
|
||||
|
@ -5,7 +5,7 @@ package: 'Testing'
|
||||
hidden: true
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -5,7 +5,7 @@ package: 'Testing'
|
||||
hidden: true
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -0,0 +1,13 @@
|
||||
name: 'Facets processors collection'
|
||||
type: module
|
||||
description: 'Contains collection of test facet processors'
|
||||
package: 'Testing'
|
||||
hidden: true
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
dependencies:
|
||||
- facets:facets
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1657367472
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The facets processors collection module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_facets_search_api_query_type_mapping_alter().
|
||||
*/
|
||||
function facets_processors_collection_facets_search_api_query_type_mapping_alter($backend_plugin_id, array &$query_types) {
|
||||
if (
|
||||
!empty($query_types['string'])
|
||||
&& $query_types['string'] === 'search_api_string'
|
||||
&& \Drupal::state()->get('facets_processors_collection_alter_string_query_handler', FALSE)
|
||||
) {
|
||||
$query_types['string'] = 'search_api_string_cached';
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
services:
|
||||
cache_context.fpc_build:
|
||||
class: Drupal\facets_processors_collection\Cache\FpcCacheContext
|
||||
argumets:
|
||||
type: build
|
||||
tags:
|
||||
- { name: cache.context }
|
||||
|
||||
cache_context.fpc_sort:
|
||||
class: Drupal\facets_processors_collection\Cache\FpcCacheContext
|
||||
argumets:
|
||||
type: sort
|
||||
tags:
|
||||
- { name: cache.context }
|
||||
|
||||
cache_context.fpc_post_query:
|
||||
class: Drupal\facets_processors_collection\Cache\FpcCacheContext
|
||||
argumets:
|
||||
type: post_query
|
||||
tags:
|
||||
- { name: cache.context }
|
||||
|
||||
cache_context.fpc_query_type_plugin:
|
||||
class: Drupal\facets_processors_collection\Cache\FpcCacheContext
|
||||
argumets:
|
||||
type: query_type_plugin
|
||||
tags:
|
||||
- { name: cache.context }
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Cache;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\Context\CacheContextInterface;
|
||||
use Drupal\facets\Processor\ProcessorInterface;
|
||||
|
||||
/**
|
||||
* Dummy cache context for the fpc_build_processor facet processor.
|
||||
*
|
||||
* Cache context IDs: fpc_build, fpc_sort, fpc_post_query.
|
||||
*/
|
||||
class FpcCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* Context type: build, sort or post_query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* List of facet processing stages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $processorStages = [
|
||||
ProcessorInterface::STAGE_POST_QUERY,
|
||||
ProcessorInterface::STAGE_BUILD,
|
||||
ProcessorInterface::STAGE_SORT,
|
||||
];
|
||||
|
||||
/**
|
||||
* Cache context type used by query type plugin.
|
||||
*/
|
||||
protected const QUERY_PLUGIN = 'query_type_plugin';
|
||||
|
||||
/**
|
||||
* FpcCacheContext constructor.
|
||||
*
|
||||
* @param string $type
|
||||
* Context type sort or build.
|
||||
*/
|
||||
public function __construct(string $type) {
|
||||
if (!in_array($type, static::getAllowedTypes())) {
|
||||
throw new \InvalidArgumentException('Valid types are: ' . implode(', ', static::getAllowedTypes()));
|
||||
}
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all allowed context types.
|
||||
*
|
||||
* @return array
|
||||
* Array of context types: all processor stages + query_type plugin.
|
||||
*/
|
||||
protected static function getAllowedTypes() {
|
||||
return array_merge(static::$processorStages, [static::QUERY_PLUGIN]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t(
|
||||
'FPC: cache context, cab be one of the following: %stages.',
|
||||
['%stages' => implode(', ', static::getAllowedTypes())]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return 'fpc_' . $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\BuildProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
|
||||
/**
|
||||
* Dummy build processor plugin to test plugin.manager cacheability.
|
||||
*
|
||||
* @FacetsProcessor(
|
||||
* id = "fpc_build_processor",
|
||||
* label = @Translation("FPC: Build test processor"),
|
||||
* description = @Translation("Adds 'test' prefix to each facet item display."),
|
||||
* stages = {
|
||||
* "build" = 50
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FpcBuildProcessor extends ProcessorPluginBase implements BuildProcessorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(FacetInterface $facet, array $results) {
|
||||
/** @var \Drupal\facets\Result\ResultInterface $result */
|
||||
foreach ($results as $result) {
|
||||
$result->setDisplayValue('Test ' . $result->getDisplayValue());
|
||||
}
|
||||
// An example cache tag that can be added from the ::build().
|
||||
$facet->addCacheTags(['fpc:added_within_build_method']);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['fpc:build_processor']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), ['fpc_build']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\PostQueryProcessorInterface;
|
||||
use Drupal\facets\Processor\ProcessorPluginBase;
|
||||
|
||||
/**
|
||||
* Dummy post query processor plugin to test plugin.manager cacheability.
|
||||
*
|
||||
* @FacetsProcessor(
|
||||
* id = "fpc_post_query_processor",
|
||||
* label = @Translation("FPC: Post query plugin"),
|
||||
* description = @Translation("Does nothing."),
|
||||
* stages = {
|
||||
* "post_query" = 50
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FpcPostQueryProcessor extends ProcessorPluginBase implements PostQueryProcessorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['fpc:post_query_processor']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), ['fpc_post_query']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postQuery(FacetInterface $facet) {
|
||||
$facet->addCacheTags(['fpc:added_within_postQuery_method']);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Plugin\facets\processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Processor\SortProcessorPluginBase;
|
||||
use Drupal\facets\Result\Result;
|
||||
|
||||
/**
|
||||
* A processor that emulates sort plugin, but does nothing with ordering.
|
||||
*
|
||||
* @FacetsProcessor(
|
||||
* id = "fpc_sort_processor",
|
||||
* label = @Translation("FPC: Sort test processor"),
|
||||
* description = @Translation("Does nothing."),
|
||||
* stages = {
|
||||
* "sort" = 50
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FpcSortProcessor extends SortProcessorPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortResults(Result $a, Result $b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return ['fpc:sort_processor'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return ['fpc_sort'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Plugin\facets\processor;
|
||||
|
||||
use Drupal\facets\Result\Result;
|
||||
|
||||
/**
|
||||
* A processor that does "random sort" plugin.
|
||||
*
|
||||
* @FacetsProcessor(
|
||||
* id = "fpc_sort_random_processor",
|
||||
* label = @Translation("FPC: random sorting"),
|
||||
* description = @Translation("Randomly sorts result, <em>disables cache</em>"),
|
||||
* stages = {
|
||||
* "sort" = 50
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FpcSortRandomProcessor extends FpcSortProcessor {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortResults(Result $a, Result $b) {
|
||||
return random_int(-1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// As sorting should be random, we can't cache results.
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\facets_processors_collection\Plugin\facets\query_type;
|
||||
|
||||
use Drupal\facets\Plugin\facets\query_type\SearchApiString;
|
||||
|
||||
/**
|
||||
* Extends base search_api_string plugin with custom cacheability.
|
||||
*
|
||||
* @FacetsQueryType(
|
||||
* id = "search_api_string_cached",
|
||||
* label = @Translation("FPC: cached string"),
|
||||
* )
|
||||
*/
|
||||
class CacheableQueryTypePlugin extends SearchApiString {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute() {
|
||||
parent::execute();
|
||||
|
||||
$this->query->addCacheTags(['fpc:query_plugin_type_plugin']);
|
||||
$this->query->addCacheContexts(['fpc_query_type_plugin']);
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@ package: 'Testing'
|
||||
hidden: true
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -0,0 +1,14 @@
|
||||
services:
|
||||
cache_context.dummy_query_build:
|
||||
class: Drupal\facets_query_processor\Cache\DummyQuery
|
||||
argumets:
|
||||
type: build
|
||||
tags:
|
||||
- { name: cache.context }
|
||||
|
||||
cache_context.dummy_query_pre_query:
|
||||
class: Drupal\facets_query_processor\Cache\DummyQuery
|
||||
argumets:
|
||||
type: pre_query
|
||||
tags:
|
||||
- { name: cache.context }
|
@ -2,8 +2,11 @@
|
||||
|
||||
namespace Drupal\facets_query_processor\Plugin\facets\url_processor;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Plugin\facets\url_processor\QueryString;
|
||||
use Drupal\facets\Utility\FacetsUrlGenerator;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
@ -21,10 +24,33 @@ class DummyQuery extends QueryString {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher) {
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher, FacetsUrlGenerator $urlGenerator) {
|
||||
// Override the default separator.
|
||||
$configuration['separator'] = '||';
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager, $eventDispatcher);
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager, $eventDispatcher, $urlGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildUrls(FacetInterface $facet, array $results) {
|
||||
$facet->addCacheTags(['dummy_query_build_urls_tag']);
|
||||
$facet->addCacheContexts(['dummy_query_build']);
|
||||
return parent::buildUrls($facet, $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return Cache::mergeTags(parent::getCacheTags(), ['dummy_query_pre_query_tag']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return Cache::mergeContexts(parent::getCacheContexts(), ['dummy_query_pre_query']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,76 +1,34 @@
|
||||
base_field: search_api_id
|
||||
base_table: search_api_index_database_search_index
|
||||
core: 8.x
|
||||
description: ''
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- search_api.index.database_search_index
|
||||
module:
|
||||
- search_api
|
||||
id: search_api_test_view
|
||||
label: 'Search API Test Fulltext search view'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: search_api_index_database_search_index
|
||||
base_field: search_api_id
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
display_plugin: default
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: none
|
||||
options: { }
|
||||
query:
|
||||
type: search_api_query
|
||||
options:
|
||||
skip_access: true
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Search
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 20, 40, 60'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ previous'
|
||||
next: 'next ›'
|
||||
first: '« first'
|
||||
last: 'last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: search_api
|
||||
options:
|
||||
view_modes:
|
||||
bundle:
|
||||
'article': default
|
||||
'page': default
|
||||
datasource:
|
||||
'entity:entity_test': default
|
||||
title: 'Fulltext test index'
|
||||
fields:
|
||||
search_api_id:
|
||||
id: search_api_id
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_id
|
||||
id: search_api_id
|
||||
plugin_id: numeric
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
plugin_id: numeric
|
||||
label: 'Entity ID'
|
||||
exclude: false
|
||||
alter:
|
||||
@ -117,9 +75,81 @@ display:
|
||||
decimal: .
|
||||
separator: ','
|
||||
format_plural: false
|
||||
format_plural_string: "1\x03@count"
|
||||
format_plural_string: !!binary MQNAY291bnQ=
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
offset: 0
|
||||
items_per_page: 10
|
||||
total_pages: null
|
||||
id: 0
|
||||
tags:
|
||||
next: 'next ›'
|
||||
previous: '‹ previous'
|
||||
first: '« first'
|
||||
last: 'last »'
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 20, 40, 60'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
quantity: 9
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Search
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: none
|
||||
options: { }
|
||||
empty: { }
|
||||
sorts:
|
||||
search_api_id:
|
||||
id: search_api_id
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
plugin_id: search_api
|
||||
order: ASC
|
||||
expose:
|
||||
label: ''
|
||||
field_identifier: search_api_id
|
||||
exposed: false
|
||||
arguments:
|
||||
search_api_datasource:
|
||||
id: search_api_datasource
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_datasource
|
||||
plugin_id: search_api
|
||||
break_phrase: true
|
||||
type:
|
||||
id: type
|
||||
table: search_api_index_database_search_index
|
||||
field: type
|
||||
plugin_id: search_api
|
||||
break_phrase: false
|
||||
not: true
|
||||
keywords:
|
||||
id: keywords
|
||||
table: search_api_index_database_search_index
|
||||
field: keywords
|
||||
plugin_id: search_api
|
||||
break_phrase: true
|
||||
filters:
|
||||
search_api_fulltext:
|
||||
id: search_api_fulltext
|
||||
@ -128,6 +158,7 @@ display:
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
plugin_id: search_api_fulltext
|
||||
operator: and
|
||||
value: ''
|
||||
group: 1
|
||||
@ -138,6 +169,8 @@ display:
|
||||
description: ''
|
||||
use_operator: true
|
||||
operator: search_api_fulltext_op
|
||||
operator_limit_selection: false
|
||||
operator_list: { }
|
||||
identifier: search_api_fulltext
|
||||
required: false
|
||||
remember: false
|
||||
@ -160,14 +193,13 @@ display:
|
||||
group_items: { }
|
||||
min_length: 3
|
||||
fields: { }
|
||||
plugin_id: search_api_fulltext
|
||||
id:
|
||||
plugin_id: search_api_numeric
|
||||
id: id
|
||||
table: search_api_index_database_search_index
|
||||
field: id
|
||||
relationship: none
|
||||
admin_label: ''
|
||||
plugin_id: search_api_numeric
|
||||
operator: '='
|
||||
group: 1
|
||||
exposed: true
|
||||
@ -177,6 +209,8 @@ display:
|
||||
description: ''
|
||||
use_operator: true
|
||||
operator: id_op
|
||||
operator_limit_selection: false
|
||||
operator_list: { }
|
||||
identifier: id
|
||||
required: false
|
||||
remember: false
|
||||
@ -187,12 +221,12 @@ display:
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
created:
|
||||
plugin_id: search_api_date
|
||||
id: created
|
||||
table: search_api_index_database_search_index
|
||||
field: created
|
||||
relationship: none
|
||||
admin_label: ''
|
||||
plugin_id: search_api_date
|
||||
operator: '='
|
||||
group: 1
|
||||
exposed: true
|
||||
@ -202,6 +236,8 @@ display:
|
||||
description: ''
|
||||
use_operator: true
|
||||
operator: created_op
|
||||
operator_limit_selection: false
|
||||
operator_list: { }
|
||||
identifier: created
|
||||
required: false
|
||||
remember: false
|
||||
@ -212,12 +248,12 @@ display:
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
keywords:
|
||||
plugin_id: search_api_string
|
||||
id: keywords
|
||||
table: search_api_index_database_search_index
|
||||
field: keywords
|
||||
relationship: none
|
||||
admin_label: ''
|
||||
plugin_id: search_api_string
|
||||
operator: '='
|
||||
group: 1
|
||||
exposed: true
|
||||
@ -227,6 +263,8 @@ display:
|
||||
description: ''
|
||||
use_operator: true
|
||||
operator: keywords_op
|
||||
operator_limit_selection: false
|
||||
operator_list: { }
|
||||
identifier: keywords
|
||||
required: false
|
||||
remember: false
|
||||
@ -237,13 +275,13 @@ display:
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
search_api_language:
|
||||
plugin_id: search_api_language
|
||||
id: search_api_language
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_language
|
||||
relationship: none
|
||||
admin_label: ''
|
||||
operator: 'in'
|
||||
plugin_id: search_api_language
|
||||
operator: in
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
@ -252,6 +290,8 @@ display:
|
||||
description: ''
|
||||
use_operator: true
|
||||
operator: language_op
|
||||
operator_limit_selection: false
|
||||
operator_list: { }
|
||||
identifier: language
|
||||
required: false
|
||||
remember: false
|
||||
@ -261,20 +301,22 @@ display:
|
||||
anonymous: '0'
|
||||
administrator: '0'
|
||||
is_grouped: false
|
||||
sorts:
|
||||
search_api_id:
|
||||
id: search_api_id
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
plugin_id: search_api
|
||||
title: 'Fulltext test index'
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: search_api
|
||||
options:
|
||||
view_modes:
|
||||
bundle:
|
||||
article: default
|
||||
page: default
|
||||
datasource:
|
||||
'entity:entity_test': default
|
||||
query:
|
||||
type: search_api_query
|
||||
options:
|
||||
skip_access: true
|
||||
relationships: { }
|
||||
header:
|
||||
result:
|
||||
id: result
|
||||
@ -283,54 +325,117 @@ display:
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
content: 'Displaying @total search results'
|
||||
plugin_id: result
|
||||
content: 'Displaying @total search results'
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments:
|
||||
search_api_datasource:
|
||||
plugin_id: search_api
|
||||
id: search_api_datasource
|
||||
table: search_api_index_database_search_index
|
||||
field: search_api_datasource
|
||||
break_phrase: true
|
||||
type:
|
||||
plugin_id: search_api
|
||||
id: type
|
||||
table: search_api_index_database_search_index
|
||||
field: type
|
||||
break_phrase: false
|
||||
not: true
|
||||
keywords:
|
||||
plugin_id: search_api
|
||||
id: keywords
|
||||
table: search_api_index_database_search_index
|
||||
field: keywords
|
||||
break_phrase: true
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
path: search-api-test-fulltext
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
block_1:
|
||||
display_plugin: block
|
||||
id: block_1
|
||||
display_title: Block
|
||||
position: 2
|
||||
display_plugin: block
|
||||
position: 3
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
defaults:
|
||||
use_ajax: false
|
||||
use_ajax: true
|
||||
label: 'Search API Test Fulltext search view'
|
||||
module: views
|
||||
id: search_api_test_view
|
||||
tag: ''
|
||||
langcode: en
|
||||
dependencies:
|
||||
module:
|
||||
- search_api
|
||||
- facets_search_api_dependency
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
block_1_sapi_tag:
|
||||
id: block_1_sapi_tag
|
||||
display_title: 'Block Search API cache tag'
|
||||
display_plugin: block
|
||||
position: 4
|
||||
display_options:
|
||||
cache:
|
||||
type: search_api_tag
|
||||
options: { }
|
||||
defaults:
|
||||
cache: false
|
||||
use_ajax: false
|
||||
use_ajax: true
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
page_1:
|
||||
id: page_1
|
||||
display_title: Page
|
||||
display_plugin: page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: search-api-test-fulltext
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
page_2_sapi_tag:
|
||||
id: page_2_sapi_tag
|
||||
display_title: 'Page Search API cache tag'
|
||||
display_plugin: page
|
||||
position: 2
|
||||
display_options:
|
||||
cache:
|
||||
type: search_api_tag
|
||||
options: { }
|
||||
defaults:
|
||||
cache: false
|
||||
display_extenders: { }
|
||||
path: search-api-test-fulltext-cache-tag
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
page_2_sapi_time:
|
||||
id: page_2_sapi_time
|
||||
display_title: 'Page Search API cache time'
|
||||
display_plugin: page
|
||||
position: 2
|
||||
display_options:
|
||||
cache:
|
||||
type: search_api_time
|
||||
options:
|
||||
results_lifespan: 21600
|
||||
results_lifespan_custom: 0
|
||||
output_lifespan: 518400
|
||||
output_lifespan_custom: 0
|
||||
defaults:
|
||||
cache: false
|
||||
display_extenders: { }
|
||||
path: search-api-test-fulltext-cache-time
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
tags:
|
||||
- 'config:search_api.index.database_search_index'
|
||||
|
@ -9,7 +9,7 @@ dependencies:
|
||||
- drupal:views
|
||||
core_version_requirement: ^9.2 || ^10.0
|
||||
|
||||
# Information added by Drupal.org packaging script on 2022-04-04
|
||||
version: '2.0.2'
|
||||
# Information added by Drupal.org packaging script on 2022-07-09
|
||||
version: '2.0.4'
|
||||
project: 'facets'
|
||||
datestamp: 1649070272
|
||||
datestamp: 1657367472
|
||||
|
@ -85,9 +85,7 @@ trait BlockTestTrait {
|
||||
* The id of the block.
|
||||
*/
|
||||
protected function deleteBlock($id) {
|
||||
// Delete a facet block through the UI, the text for the success message has
|
||||
// changed in Drupal::VERSION 9.3.
|
||||
$orig_success_message = 'The block ' . $this->blocks[$id]->label() . ' has been removed' . (\Drupal::VERSION >= 9.3 ? ' from the Footer region' : '') . '.';
|
||||
$orig_success_message = 'The block ' . $this->blocks[$id]->label() . ' has been removed from the Footer region.';
|
||||
|
||||
$this->drupalGet('admin/structure/block/manage/' . $this->blocks[$id]->id(), ['query' => ['destination' => 'admin']]);
|
||||
$this->clickLink('Remove block');
|
||||
|
@ -121,7 +121,7 @@ class BreadcrumbIntegrationTest extends FacetsTestBase {
|
||||
*/
|
||||
protected function editFacetConfig(array $config = []) {
|
||||
$this->drupalGet('admin/config/search/facets');
|
||||
$this->clickLink('Configure', 1);
|
||||
$this->clickLink('Configure', 2);
|
||||
$default_config = [
|
||||
'filter_key' => 'f',
|
||||
'url_processor' => 'query_string',
|
||||
|
@ -383,7 +383,7 @@ class HierarchicalFacetIntegrationTest extends FacetsTestBase {
|
||||
*/
|
||||
public function testHierarchyBreadcrumb() {
|
||||
$this->drupalGet('admin/config/search/facets');
|
||||
$this->clickLink('Configure', 1);
|
||||
$this->clickLink('Configure', 2);
|
||||
$default_config = [
|
||||
'filter_key' => 'f',
|
||||
'url_processor' => 'query_string',
|
||||
|
@ -0,0 +1,741 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\facets\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\facets\FacetInterface;
|
||||
use Drupal\facets\Plugin\facets\query_type\SearchApiDate;
|
||||
|
||||
/**
|
||||
* Tests facets functionality that have search_api view with search_api_cache.
|
||||
*
|
||||
* @group facets
|
||||
*/
|
||||
class IntegrationCacheTest extends FacetsTestBase {
|
||||
|
||||
/**
|
||||
* Views view url with search_api_tag cache plugin.
|
||||
*/
|
||||
protected const VIEW_URL = 'search-api-test-fulltext-cache-tag';
|
||||
|
||||
/**
|
||||
* Views view display id with search_api_tag cache plugin.
|
||||
*/
|
||||
protected const VIEW_DISPLAY = 'page_2_sapi_tag';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'views',
|
||||
'node',
|
||||
'search_api',
|
||||
'facets',
|
||||
'block',
|
||||
'facets_search_api_dependency',
|
||||
'taxonomy',
|
||||
'page_cache',
|
||||
];
|
||||
|
||||
/**
|
||||
* Facets entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorage
|
||||
*/
|
||||
protected $facetStorage;
|
||||
|
||||
/**
|
||||
* The entity_test_mulrev_changed entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
|
||||
*/
|
||||
protected $entityTestStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->enableWebsiteCache();
|
||||
$this->setUpExampleStructure();
|
||||
$this->insertExampleContent();
|
||||
$this->assertEquals(5, $this->indexItems($this->indexId), '5 items were indexed.');
|
||||
|
||||
$this->facetStorage = $this->container->get('entity_type.manager')
|
||||
->getStorage('facets_facet');
|
||||
$this->entityTestStorage = \Drupal::entityTypeManager()
|
||||
->getStorage('entity_test_mulrev_changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests various operations via the Facets' admin UI.
|
||||
*
|
||||
* Cached implementation of testBlockView integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testBlockView()
|
||||
*/
|
||||
public function testFramework() {
|
||||
$facet_id = 'test_facet_name';
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
// By default, the view should show all entities.
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
|
||||
$this->createFacet('Test Facet name', $facet_id, 'type', static::VIEW_DISPLAY);
|
||||
|
||||
// Verify that the facet results are correct.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('item');
|
||||
$this->assertSession()->pageTextContains('article');
|
||||
|
||||
// Verify that facet blocks appear as expected.
|
||||
$this->assertFacetBlocksAppear();
|
||||
|
||||
// Verify that the facet only shows when the facet source is visible, it
|
||||
// should not show up on the user page.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertNoFacetBlocksAppear();
|
||||
|
||||
// Do not show the block on empty behaviors.
|
||||
$this->clearIndex();
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
|
||||
// Verify that no facet blocks appear. Empty behavior "None" is selected by
|
||||
// default.
|
||||
$this->assertNoFacetBlocksAppear();
|
||||
|
||||
// Verify that the "empty_text" appears as expected.
|
||||
$settings = [
|
||||
'behavior' => 'text',
|
||||
'text' => 'No results found for this block!',
|
||||
'text_format' => 'plain_text',
|
||||
];
|
||||
$facet = $this->getFacetById($facet_id);
|
||||
$facet->setEmptyBehavior($settings);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->responseContains('block-test-facet-name');
|
||||
$this->assertSession()->responseContains('No results found for this block!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a block view also works.
|
||||
*
|
||||
* Cached implementation of testBlockView integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testBlockView()
|
||||
*/
|
||||
public function testBlockView() {
|
||||
$webAssert = $this->assertSession();
|
||||
$this->createFacet(
|
||||
'Block view facet',
|
||||
'block_view_facet',
|
||||
'type',
|
||||
'block_1_sapi_tag',
|
||||
'views_block__search_api_test_view'
|
||||
);
|
||||
|
||||
// Place the views block in the footer of all pages.
|
||||
$block_settings = [
|
||||
'region' => 'sidebar_first',
|
||||
'id' => 'view_block',
|
||||
];
|
||||
$this->drupalPlaceBlock('views_block:search_api_test_view-block_1_sapi_tag', $block_settings);
|
||||
|
||||
// By default, the view should show all entities.
|
||||
$this->drupalGet('<front>');
|
||||
$webAssert->pageTextContains('Fulltext test index');
|
||||
$webAssert->pageTextContains('Displaying 5 search results');
|
||||
$webAssert->pageTextContains('item');
|
||||
$webAssert->pageTextContains('article');
|
||||
|
||||
// Click the item link, and test that filtering of results actually works.
|
||||
$this->clickLink('item');
|
||||
$webAssert->pageTextContains('Displaying 3 search results');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an url alias works correctly.
|
||||
*
|
||||
* Cached implementation of testUrlAlias integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testUrlAlias()
|
||||
*/
|
||||
public function testUrlAlias() {
|
||||
$facet_id = 'ab_facet';
|
||||
$this->createFacet('ab Facet', $facet_id, 'type', static::VIEW_DISPLAY);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
|
||||
$this->clickLink('item');
|
||||
$url = Url::fromUserInput('/' . static::VIEW_URL, ['query' => ['f' => ['ab_facet:item']]]);
|
||||
$this->assertSession()->addressEquals($url);
|
||||
|
||||
$this->updateFacet($facet_id, ['url_alias' => 'llama']);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
|
||||
$this->clickLink('item');
|
||||
$url = Url::fromUserInput('/' . static::VIEW_URL, ['query' => ['f' => ['llama:item']]]);
|
||||
$this->assertSession()->addressEquals($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests facet dependencies.
|
||||
*
|
||||
* Cached implementation of testFacetDependencies integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testFacetDependencies()
|
||||
*/
|
||||
public function testFacetDependencies() {
|
||||
$facet_name = "DependableFacet";
|
||||
$facet_id = 'dependablefacet';
|
||||
|
||||
$depending_facet_name = "DependingFacet";
|
||||
$depending_facet_id = "dependingfacet";
|
||||
|
||||
$this->createFacet($facet_name, $facet_id, 'type', static::VIEW_DISPLAY);
|
||||
$this->createFacet($depending_facet_name, $depending_facet_id, 'keywords', static::VIEW_DISPLAY);
|
||||
|
||||
// Go to the view and test that both facets are shown. Item and article
|
||||
// come from the DependableFacet, orange and grape come from DependingFacet.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
$this->assertFacetBlocksAppear();
|
||||
|
||||
// Change the visiblity settings of the DependingFacet.
|
||||
$facet = $this->getFacetById($depending_facet_id);
|
||||
$processor = [
|
||||
'processor_id' => 'dependent_processor',
|
||||
'weights' => ['build' => 5],
|
||||
'settings' => [
|
||||
$facet_id => [
|
||||
'enable' => TRUE,
|
||||
'condition' => 'values',
|
||||
'values' => 'item',
|
||||
'negate' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
$facet->addProcessor($processor);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
// Go to the view and test that only the types are shown.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->linkNotExists('grape');
|
||||
$this->assertSession()->linkNotExists('orange');
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
|
||||
// Click on the item, and test that this shows the keywords.
|
||||
$this->clickLink('item');
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
|
||||
// Go back to the view, click on article and test that the keywords are
|
||||
// hidden.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->clickLink('article');
|
||||
$this->assertSession()->linkNotExists('grape');
|
||||
$this->assertSession()->linkNotExists('orange');
|
||||
|
||||
// Change the visibility settings to negate the previous settings.
|
||||
$processor['settings'][$facet_id]['negate'] = TRUE;
|
||||
$facet->addProcessor($processor);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
// Go to the view and test only the type facet is shown.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
|
||||
// Click on the article, and test that this shows the keywords.
|
||||
$this->clickLink('article');
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
|
||||
// Go back to the view, click on item and test that the keywords are
|
||||
// hidden.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->clickLink('item');
|
||||
$this->assertSession()->linkNotExists('grape');
|
||||
$this->assertSession()->linkNotExists('orange');
|
||||
|
||||
// Disable negation again.
|
||||
$processor['settings'][$facet_id]['negate'] = FALSE;
|
||||
$facet->addProcessor($processor);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertSession()->linkNotExists('grape');
|
||||
$this->clickLink('item');
|
||||
$this->assertSession()->pageTextContains('Displaying 3 search results');
|
||||
$this->assertSession()->linkExists('grape');
|
||||
$this->clickLink('grape');
|
||||
$this->assertSession()->pageTextContains('Displaying 1 search results');
|
||||
// Disable item again, and the grape should not be reflected in the search
|
||||
// result anymore.
|
||||
$this->clickLink('item');
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the facet's and/or functionality.
|
||||
*
|
||||
* Cached implementation of testAndOrFacet integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testAndOrFacet()
|
||||
*/
|
||||
public function testAndOrFacet() {
|
||||
$facet_id = 'test_facet';
|
||||
|
||||
$this->createFacet('test & facet', $facet_id, 'type', static::VIEW_DISPLAY);
|
||||
$this->updateFacet($facet_id, ['query_operator' => 'and']);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
|
||||
$this->clickLink('item');
|
||||
$this->checkFacetIsActive('item');
|
||||
$this->assertSession()->linkNotExists('article');
|
||||
|
||||
$this->updateFacet($facet_id, ['query_operator' => 'or']);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetLabel('item');
|
||||
$this->assertFacetLabel('article');
|
||||
|
||||
$this->clickLink('item (3)');
|
||||
$this->checkFacetIsActive('item');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the facet's exclude functionality.
|
||||
*
|
||||
* Cached implementation of testExcludeFacet integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testExcludeFacet()
|
||||
*/
|
||||
public function testExcludeFacet() {
|
||||
$facet_id = 'test_facet';
|
||||
$this->createFacet('test & facet', $facet_id, 'type', static::VIEW_DISPLAY);
|
||||
$this->updateFacet($facet_id, ['exclude' => TRUE]);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('foo bar baz');
|
||||
$this->assertSession()->pageTextContains('foo baz');
|
||||
$this->assertFacetLabel('item');
|
||||
|
||||
$this->clickLink('item');
|
||||
$this->checkFacetIsActive('item');
|
||||
$this->assertSession()->pageTextContains('foo baz');
|
||||
$this->assertSession()->pageTextContains('bar baz');
|
||||
$this->assertSession()->pageTextNotContains('foo bar baz');
|
||||
|
||||
$this->updateFacet($facet_id, ['exclude' => FALSE]);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('foo bar baz');
|
||||
$this->assertSession()->pageTextContains('foo baz');
|
||||
$this->assertFacetLabel('item');
|
||||
|
||||
$this->clickLink('item');
|
||||
$this->checkFacetIsActive('item');
|
||||
$this->assertSession()->pageTextContains('foo bar baz');
|
||||
$this->assertSession()->pageTextContains('foo test');
|
||||
$this->assertSession()->pageTextContains('bar');
|
||||
$this->assertSession()->pageTextNotContains('foo baz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the facet's exclude functionality for a date field.
|
||||
*
|
||||
* Cached implementation of testExcludeFacetDate integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testExcludeFacetDate()
|
||||
*/
|
||||
public function testExcludeFacetDate() {
|
||||
$facet_id = $field_name = 'created';
|
||||
|
||||
$this->entityTestStorage->create([
|
||||
'name' => 'foo new',
|
||||
'body' => 'test test',
|
||||
'type' => 'item',
|
||||
'keywords' => ['orange'],
|
||||
'category' => 'item_category',
|
||||
$field_name => 1490000000,
|
||||
])->save();
|
||||
|
||||
$this->entityTestStorage->create([
|
||||
'name' => 'foo old',
|
||||
'body' => 'test test',
|
||||
'type' => 'item',
|
||||
'keywords' => ['orange'],
|
||||
'category' => 'item_category',
|
||||
$field_name => 1460000000,
|
||||
])->save();
|
||||
|
||||
$this->assertEquals(2, $this->indexItems($this->indexId), '2 items were indexed.');
|
||||
|
||||
$this->createFacet('Created', $facet_id, $field_name, static::VIEW_DISPLAY);
|
||||
$facet = $this->getFacetById($facet_id);
|
||||
$facet->addProcessor([
|
||||
'processor_id' => 'date_item',
|
||||
'weights' => ['build' => 35],
|
||||
'settings' => [
|
||||
'date_display' => 'actual_date',
|
||||
'granularity' => SearchApiDate::FACETAPI_DATE_MONTH,
|
||||
'hierarchy' => FALSE,
|
||||
'date_format' => '',
|
||||
],
|
||||
]);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('foo old');
|
||||
$this->assertSession()->pageTextContains('foo new');
|
||||
$this->clickLink('March 2017');
|
||||
$this->checkFacetIsActive('March 2017');
|
||||
$this->assertSession()->pageTextContains('foo new');
|
||||
$this->assertSession()->pageTextNotContains('foo old');
|
||||
|
||||
$this->updateFacet($facet->id(), ['exclude' => TRUE]);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->clickLink('March 2017');
|
||||
$this->checkFacetIsActive('March 2017');
|
||||
$this->assertSession()->pageTextContains('foo old');
|
||||
$this->assertSession()->pageTextNotContains('foo new');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests allow only one active item.
|
||||
*
|
||||
* Cached implementation of testAllowOneActiveItem integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testAllowOneActiveItem()
|
||||
*/
|
||||
public function testAllowOneActiveItem() {
|
||||
$this->createFacet('Spotted wood owl', 'spotted_wood_owl', 'keywords', static::VIEW_DISPLAY);
|
||||
|
||||
$facet = $this->getFacetById('spotted_wood_owl');
|
||||
$facet->setShowOnlyOneResult(TRUE);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
|
||||
$this->clickLink('grape');
|
||||
$this->assertSession()->pageTextContains('Displaying 3 search results');
|
||||
$this->checkFacetIsActive('grape');
|
||||
$this->assertFacetLabel('orange');
|
||||
|
||||
$this->clickLink('orange');
|
||||
$this->assertSession()->pageTextContains('Displaying 3 search results');
|
||||
$this->assertFacetLabel('grape');
|
||||
$this->checkFacetIsActive('orange');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calculations of facet count.
|
||||
*
|
||||
* Cached implementation of testFacetCountCalculations integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testFacetCountCalculations()
|
||||
*/
|
||||
public function testFacetCountCalculations() {
|
||||
$this->createFacet('Type', 'type', 'type', static::VIEW_DISPLAY);
|
||||
$this->createFacet('Keywords', 'keywords', 'keywords', static::VIEW_DISPLAY);
|
||||
foreach (['type', 'keywords'] as $facet_id) {
|
||||
$this->updateFacet($facet_id, ['query_operator' => 'and']);
|
||||
}
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('grape (3)');
|
||||
|
||||
// Make sure that after clicking on article, which has only 2 entities,
|
||||
// there are only 2 items left in the results for other facets as well.
|
||||
// In this case, that means we can't have 3 entities tagged with grape. Both
|
||||
// remaining entities are tagged with grape and strawberry.
|
||||
$this->clickPartialLink('article');
|
||||
$this->assertSession()->pageTextNotContains('(3)');
|
||||
$this->assertFacetLabel('grape (2)');
|
||||
$this->assertFacetLabel('strawberry (2)');
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('grape (3)');
|
||||
|
||||
// Make sure that after clicking on grape, which has only 3 entities, there
|
||||
// are only 3 items left in the results for other facets as well. In this
|
||||
// case, that means 2 entities of type article and 1 item.
|
||||
$this->clickPartialLink('grape');
|
||||
$this->assertSession()->pageTextContains('Displaying 3 search results');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item (1)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the hard limit setting.
|
||||
*
|
||||
* Cached implementation of testHardLimit integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testHardLimit()
|
||||
*/
|
||||
public function testHardLimit() {
|
||||
$this->createFacet('Owl', 'owl', 'keywords', static::VIEW_DISPLAY);
|
||||
$facet = $this->getFacetById('owl');
|
||||
$facet->addProcessor([
|
||||
'processor_id' => 'active_widget_order',
|
||||
'weights' => ['sort' => 20],
|
||||
'settings' => [],
|
||||
]);
|
||||
$facet->addProcessor([
|
||||
'processor_id' => 'display_value_widget_order',
|
||||
'weights' => ['build' => 40],
|
||||
'settings' => [],
|
||||
]);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('grape (3)');
|
||||
$this->assertFacetLabel('orange (3)');
|
||||
$this->assertFacetLabel('apple (2)');
|
||||
$this->assertFacetLabel('banana (1)');
|
||||
$this->assertFacetLabel('strawberry (2)');
|
||||
|
||||
$this->updateFacet($facet->id(), ['hard_limit' => 3]);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
// We're still testing for 5 search results here, the hard limit only limits
|
||||
// the facets, not the search results.
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('grape (3)');
|
||||
$this->assertFacetLabel('orange (3)');
|
||||
$this->assertFacetLabel('apple (2)');
|
||||
$this->assertSession()->pageTextNotContains('banana (0)');
|
||||
$this->assertSession()->pageTextNotContains('strawberry (0)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test minimum amount of items.
|
||||
*
|
||||
* Cached implementation of testMinimumAmount integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testMinimumAmount()
|
||||
*/
|
||||
public function testMinimumAmount() {
|
||||
$this->createFacet('Elf owl', 'elf_owl', 'type', static::VIEW_DISPLAY);
|
||||
$this->updateFacet('elf_owl', ['min_count' => 1]);
|
||||
|
||||
// See that both article and item are showing.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item (3)');
|
||||
|
||||
$this->updateFacet('elf_owl', ['min_count' => 3]);
|
||||
|
||||
// See that article is now hidden, item should still be showing.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertSession()->pageTextNotContains('article');
|
||||
$this->assertFacetLabel('item (3)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the visibility of facet source.
|
||||
*
|
||||
* Cached implementation of testFacetSourceVisibility integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testFacetSourceVisibility()
|
||||
*/
|
||||
public function testFacetSourceVisibility() {
|
||||
$this->createFacet('Vicuña', 'vicuna', 'type', static::VIEW_DISPLAY);
|
||||
// Facet appears only on the search page for which it was created.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetBlocksAppear();
|
||||
$this->drupalGet('');
|
||||
$this->assertNoFacetBlocksAppear();
|
||||
|
||||
$facet = $this->getFacetById('vicuna');
|
||||
$facet->setOnlyVisibleWhenFacetSourceIsVisible(FALSE);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
// Test that the facet source is visible on the search page and user/2 page.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertFacetBlocksAppear();
|
||||
$this->drupalGet('');
|
||||
$this->assertFacetBlocksAppear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests behavior with multiple enabled facets and their interaction.
|
||||
*
|
||||
* Cached implementation of testMultipleFacets integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testMultipleFacets()
|
||||
*/
|
||||
public function testMultipleFacets() {
|
||||
// Create 2 facets.
|
||||
$this->createFacet('Snow Owl', 'snow_owl', 'type', static::VIEW_DISPLAY);
|
||||
$this->createFacet('Forest Owl', 'forest_owl', 'category', static::VIEW_DISPLAY);
|
||||
|
||||
foreach (['snow_owl', 'forest_owl'] as $facet_id) {
|
||||
$this->updateFacet($facet_id, ['min_count' => 0]);
|
||||
}
|
||||
|
||||
// Go to the view and check the default behavior.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->assertFacetLabel('item (3)');
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item_category (2)');
|
||||
$this->assertFacetLabel('article_category (2)');
|
||||
|
||||
// Start filtering.
|
||||
$this->clickPartialLink('item_category');
|
||||
$this->assertSession()->pageTextContains('Displaying 2 search results');
|
||||
$this->checkFacetIsActive('item_category');
|
||||
$this->assertFacetLabel('item (2)');
|
||||
|
||||
// Go back to the overview and start another filter, from the second facet
|
||||
// block this time.
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextContains('Displaying 5 search results');
|
||||
$this->clickPartialLink('article (2)');
|
||||
$this->assertSession()->pageTextContains('Displaying 2 search results');
|
||||
$this->checkFacetIsActive('article');
|
||||
$this->assertFacetLabel('article_category (2)');
|
||||
$this->assertFacetLabel('item_category (0)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the configuration for showing a title works.
|
||||
*
|
||||
* Cached implementation of testShowTitle integration test.
|
||||
*
|
||||
* @see \Drupal\Tests\facets\Functional\IntegrationTest::testShowTitle()
|
||||
*/
|
||||
public function testShowTitle() {
|
||||
$this->createFacet('Llama', 'llama', 'type', static::VIEW_DISPLAY);
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->pageTextNotContains('Llama');
|
||||
|
||||
$this->updateFacet('llama', ['show_title' => TRUE]);
|
||||
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()->responseContains('<h3>Llama</h3>');
|
||||
$this->assertSession()->pageTextContains('Llama');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test facet blocks cache invalidation.
|
||||
*
|
||||
* Test covers search page with a facets and standalone facet block on FP.
|
||||
*/
|
||||
public function testFacetBlockCacheNewContentIndexing() {
|
||||
$this->createFacet('Test Facet name', 'test_facet_name', 'type', static::VIEW_DISPLAY);
|
||||
|
||||
$facet = $this->getFacetById('test_facet_name');
|
||||
$facet->setOnlyVisibleWhenFacetSourceIsVisible(FALSE);
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
foreach (['', static::VIEW_URL] as $url) {
|
||||
$this->drupalGet($url);
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item (3)');
|
||||
}
|
||||
|
||||
$this->entityTestStorage->create([
|
||||
'name' => 'foo jiz baz',
|
||||
'body' => 'test test and a bit more test',
|
||||
'type' => 'item',
|
||||
'keywords' => ['orange', 'black'],
|
||||
'category' => 'item_category',
|
||||
])->save();
|
||||
|
||||
// Entity was added but not indexed yet, so facet state should remain the
|
||||
// same.
|
||||
foreach (['', static::VIEW_URL] as $url) {
|
||||
$this->drupalGet($url);
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item (3)');
|
||||
}
|
||||
|
||||
// Index 1 remaining item and check that count has been updated.
|
||||
$this->assertEquals(1, $this->indexItems($this->indexId), '1 item was indexed.');
|
||||
foreach (['', static::VIEW_URL] as $url) {
|
||||
$this->drupalGet($url);
|
||||
$this->assertFacetLabel('article (2)');
|
||||
$this->assertFacetLabel('item (4)');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable website page caching, set 1 day max age.
|
||||
*/
|
||||
protected function enableWebsiteCache() {
|
||||
$max_age = 86400;
|
||||
$this->config('system.performance')
|
||||
->set('cache.page.max_age', $max_age)
|
||||
->save();
|
||||
$this->drupalGet(static::VIEW_URL);
|
||||
$this->assertSession()
|
||||
->responseHeaderContains('Cache-Control', 'max-age=' . $max_age);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facet entity by ids.
|
||||
*
|
||||
* @param string $id
|
||||
* Facet id.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* Loaded facet object.
|
||||
*/
|
||||
protected function getFacetById(string $id): FacetInterface {
|
||||
return $this->facetStorage->load($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update facet tith with given values.
|
||||
*
|
||||
* @param string $id
|
||||
* The facet entity ID.
|
||||
* @param array $settings
|
||||
* Array with values keyed by property names.
|
||||
*
|
||||
* @return \Drupal\facets\FacetInterface
|
||||
* An updated facet entity.
|
||||
*/
|
||||
protected function updateFacet(string $id, array $settings): FacetInterface {
|
||||
$facet = $this->getFacetById($id);
|
||||
foreach ($settings as $name => $value) {
|
||||
$facet->set($name, $value);
|
||||
}
|
||||
$this->facetStorage->save($facet);
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user