class QueryString in Facets 8
Query string URL processor.
Plugin annotation
@FacetsUrlProcessor(
id = "query_string",
label = @Translation("Query string"),
description = @Translation("Query string is the default Facets URL processor, and uses GET parameters, for example ?f[0]=brand:drupal&f[1]=color:blue")
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\facets\Processor\ProcessorPluginBase implements ProcessorInterface uses DependencyTrait
- class \Drupal\facets\UrlProcessor\UrlProcessorPluginBase implements ContainerFactoryPluginInterface, UrlProcessorInterface
- class \Drupal\facets\Plugin\facets\url_processor\QueryString
- class \Drupal\facets\UrlProcessor\UrlProcessorPluginBase implements ContainerFactoryPluginInterface, UrlProcessorInterface
- class \Drupal\facets\Processor\ProcessorPluginBase implements ProcessorInterface uses DependencyTrait
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of QueryString
3 files declare their use of QueryString
- DummyQuery.php in tests/
facets_query_processor/ src/ Plugin/ facets/ url_processor/ DummyQuery.php - QueryStringTest.php in tests/
src/ Unit/ Plugin/ url_processor/ QueryStringTest.php - RangeSliderProcessorTest.php in modules/
facets_range_widget/ tests/ src/ Unit/ Plugin/ processor/ RangeSliderProcessorTest.php
File
- src/
Plugin/ facets/ url_processor/ QueryString.php, line 25
Namespace
Drupal\facets\Plugin\facets\url_processorView source
class QueryString extends UrlProcessorPluginBase {
/**
* A string of how to represent the facet in the url.
*
* @var string
*/
protected $urlAlias;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager);
$this->eventDispatcher = $eventDispatcher;
$this
->initializeActiveFilters();
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('request_stack')
->getMasterRequest(), $container
->get('entity_type.manager'), $container
->get('event_dispatcher'));
}
/**
* {@inheritdoc}
*/
public function buildUrls(FacetInterface $facet, array $results) {
// No results are found for this facet, so don't try to create urls.
if (empty($results)) {
return [];
}
// First get the current list of get parameters.
$get_params = $this->request->query;
// When adding/removing a filter the number of pages may have changed,
// possibly resulting in an invalid page parameter.
if ($get_params
->has('page')) {
$current_page = $get_params
->get('page');
$get_params
->remove('page');
}
// Set the url alias from the facet object.
$this->urlAlias = $facet
->getUrlAlias();
$facet_source_path = $facet
->getFacetSource()
->getPath();
$request = $this
->getRequestByFacetSourcePath($facet_source_path);
$requestUrl = $this
->getUrlForRequest($facet_source_path, $request);
$original_filter_params = [];
foreach ($this
->getActiveFilters() as $facet_id => $values) {
$values = array_filter($values, static function ($it) {
return $it !== NULL;
});
foreach ($values as $value) {
$original_filter_params[] = $this
->getUrlAliasByFacetId($facet_id, $facet
->getFacetSourceId()) . ":" . $value;
}
}
/** @var \Drupal\facets\Result\ResultInterface[] $results */
foreach ($results as &$result) {
// Reset the URL for each result.
$url = clone $requestUrl;
// Sets the url for children.
if ($children = $result
->getChildren()) {
$this
->buildUrls($facet, $children);
}
if ($result
->getRawValue() === NULL) {
$filter_string = NULL;
}
else {
$filter_string = $this->urlAlias . $this
->getSeparator() . $result
->getRawValue();
}
$result_get_params = clone $get_params;
$filter_params = $original_filter_params;
// 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) {
unset($filter_params[$key]);
}
}
if ($facet
->getUseHierarchy()) {
$id = $result
->getRawValue();
// Disable child filters.
foreach ($facet
->getHierarchyInstance()
->getNestedChildIds($id) as $child_id) {
$filter_params = array_diff($filter_params, [
$this->urlAlias . $this
->getSeparator() . $child_id,
]);
}
if ($facet
->getEnableParentWhenChildGetsDisabled()) {
// Enable parent id again if exists.
$parent_ids = $facet
->getHierarchyInstance()
->getParentIds($id);
if (isset($parent_ids[0]) && $parent_ids[0]) {
// Get the parents children.
$child_ids = $facet
->getHierarchyInstance()
->getNestedChildIds($parent_ids[0]);
// Check if there are active siblings.
$active_sibling = FALSE;
if ($child_ids) {
foreach ($results as $result2) {
if ($result2
->isActive() && $result2
->getRawValue() != $id && in_array($result2
->getRawValue(), $child_ids)) {
$active_sibling = TRUE;
continue;
}
}
}
if (!$active_sibling) {
$filter_params[] = $this->urlAlias . $this
->getSeparator() . $parent_ids[0];
}
}
}
}
}
else {
if ($filter_string !== NULL) {
$filter_params[] = $filter_string;
}
$parents_and_child_ids = [];
if ($facet
->getUseHierarchy()) {
$parent_ids = $facet
->getHierarchyInstance()
->getParentIds($result
->getRawValue());
$child_ids = $facet
->getHierarchyInstance()
->getNestedChildIds($result
->getRawValue());
$parents_and_child_ids = array_merge($parent_ids, $child_ids);
if (!$facet
->getKeepHierarchyParentsActive()) {
// If hierarchy is active, unset parent trail and every child when
// building the enable-link to ensure those are not enabled anymore.
foreach ($parents_and_child_ids as $id) {
$filter_params = array_diff($filter_params, [
$this->urlAlias . $this
->getSeparator() . $id,
]);
}
}
}
// Exclude currently active results from the filter params if we are in
// the show_only_one_result mode.
if ($facet
->getShowOnlyOneResult()) {
foreach ($results as $result2) {
if ($result2
->isActive()) {
$id = $result2
->getRawValue();
if (!in_array($id, $parents_and_child_ids)) {
$active_filter_string = $this->urlAlias . $this
->getSeparator() . $id;
foreach ($filter_params as $key2 => $filter_param2) {
if ($filter_param2 == $active_filter_string) {
unset($filter_params[$key2]);
}
}
}
}
}
}
}
// Allow other modules to alter the result url built.
$event = new QueryStringCreated($result_get_params, $filter_params, $result, $this->activeFilters, $facet);
$this->eventDispatcher
->dispatch(QueryStringCreated::NAME, $event);
$filter_params = $event
->getFilterParameters();
asort($filter_params, \SORT_NATURAL);
$result_get_params
->set($this->filterKey, array_values($filter_params));
if ($result_get_params
->all() !== [
$this->filterKey => [],
]) {
$new_url_params = $result_get_params
->all();
if (empty($new_url_params[$this->filterKey])) {
unset($new_url_params[$this->filterKey]);
}
// Facet links should be page-less.
// See https://www.drupal.org/node/2898189.
unset($new_url_params['page']);
// Remove core wrapper format (e.g. render-as-ajax-response) paremeters.
unset($new_url_params[MainContentViewSubscriber::WRAPPER_FORMAT]);
// Set the new url parameters.
$url
->setOption('query', $new_url_params);
}
$result
->setUrl($url);
}
// Restore page parameter again. See https://www.drupal.org/node/2726455.
if (isset($current_page)) {
$get_params
->set('page', $current_page);
}
return $results;
}
/**
* Gets a request object based on the facet source path.
*
* If the facet's source has a path, we construct a request object based on
* that path, as it may be different than the current request's. This method
* statically caches the request object based on the facet source path so that
* subsequent calls to this processer do not recreate the same request object.
*
* @param string $facet_source_path
* The facet source path.
*
* @return \Symfony\Component\HttpFoundation\Request
* The request.
*/
protected function getRequestByFacetSourcePath($facet_source_path) {
$requestsByPath =& drupal_static(__CLASS__ . __FUNCTION__, []);
if (!$facet_source_path) {
return $this->request;
}
if (array_key_exists($facet_source_path, $requestsByPath)) {
return $requestsByPath[$facet_source_path];
}
$request = Request::create($facet_source_path);
$request->attributes
->set('_format', $this->request
->get('_format'));
$requestsByPath[$facet_source_path] = $request;
return $request;
}
/**
* 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 string $facet_source_path
* The facet source path.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @return \Drupal\Core\Url
* 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;
}
/**
* Initializes the active filters from the request query.
*
* Get all the filters that are active by checking the request query and store
* them in activeFilters which is an array where key is the facet id and value
* is an array of raw values.
*/
protected function initializeActiveFilters() {
$url_parameters = $this->request->query;
// Get the active facet parameters.
$active_params = $url_parameters
->get($this->filterKey, [], TRUE);
$facet_source_id = $this->configuration['facet']
->getFacetSourceId();
// When an invalid parameter is passed in the url, we can't do anything.
if (!is_array($active_params)) {
return;
}
// Explode the active params on the separator.
foreach ($active_params as $param) {
$explosion = explode($this
->getSeparator(), $param);
$url_alias = array_shift($explosion);
$facet_id = $this
->getFacetIdByUrlAlias($url_alias, $facet_source_id);
$value = '';
while (count($explosion) > 0) {
$value .= array_shift($explosion);
if (count($explosion) > 0) {
$value .= $this
->getSeparator();
}
}
if (!isset($this->activeFilters[$facet_id])) {
$this->activeFilters[$facet_id] = [
$value,
];
}
else {
$this->activeFilters[$facet_id][] = $value;
}
}
}
/**
* Gets the facet id from the url alias & facet source id.
*
* @param string $url_alias
* The url alias.
* @param string $facet_source_id
* The facet source id.
*
* @return bool|string
* Either the facet id, or FALSE if that can't be loaded.
*/
protected function getFacetIdByUrlAlias($url_alias, $facet_source_id) {
$mapping =& drupal_static(__FUNCTION__);
if (!isset($mapping[$facet_source_id][$url_alias])) {
$storage = $this->entityTypeManager
->getStorage('facets_facet');
$facet = current($storage
->loadByProperties([
'url_alias' => $url_alias,
'facet_source_id' => $facet_source_id,
]));
if (!$facet) {
return NULL;
}
$mapping[$facet_source_id][$url_alias] = $facet
->id();
}
return $mapping[$facet_source_id][$url_alias];
}
/**
* Gets the url alias from the facet id & facet source id.
*
* @param string $facet_id
* The facet id.
* @param string $facet_source_id
* The facet source id.
*
* @return bool|string
* Either the url alias, or FALSE if that can't be loaded.
*/
protected function getUrlAliasByFacetId($facet_id, $facet_source_id) {
$mapping =& drupal_static(__FUNCTION__);
if (!isset($mapping[$facet_source_id][$facet_id])) {
$storage = $this->entityTypeManager
->getStorage('facets_facet');
$facet = current($storage
->loadByProperties([
'id' => $facet_id,
'facet_source_id' => $facet_source_id,
]));
if (!$facet) {
return FALSE;
}
$mapping[$facet_source_id][$facet_id] = $facet
->getUrlAlias();
}
return $mapping[$facet_source_id][$facet_id];
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
DependencyTrait:: |
protected | property | The object's dependencies. | |
DependencyTrait:: |
protected | function | Adds multiple dependencies. | |
DependencyTrait:: |
protected | function | Adds a dependency. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
ProcessorInterface:: |
constant | Processing stage: build. | ||
ProcessorInterface:: |
constant | Processing stage: post_query. | ||
ProcessorInterface:: |
constant | Processing stage: pre_query. | ||
ProcessorInterface:: |
constant | Processing stage: sort. | ||
ProcessorPluginBase:: |
public | function |
Adds a configuration form for this processor. Overrides ProcessorInterface:: |
10 |
ProcessorPluginBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
|
ProcessorPluginBase:: |
public | function |
Gets default configuration for this plugin. Overrides ConfigurableInterface:: |
8 |
ProcessorPluginBase:: |
public | function |
Gets this plugin's configuration. Overrides ConfigurableInterface:: |
|
ProcessorPluginBase:: |
public | function |
Returns the default weight for a specific processing stage. Overrides ProcessorInterface:: |
|
ProcessorPluginBase:: |
public | function |
Retrieves the processor description. Overrides ProcessorInterface:: |
|
ProcessorPluginBase:: |
public | function |
Picks the preferred query type for this widget. Overrides ProcessorInterface:: |
4 |
ProcessorPluginBase:: |
public | function |
Determines whether this processor should be hidden from the user. Overrides ProcessorInterface:: |
|
ProcessorPluginBase:: |
public | function |
Determines whether this processor should always be enabled. Overrides ProcessorInterface:: |
|
ProcessorPluginBase:: |
public | function |
Sets the configuration for this plugin instance. Overrides ConfigurableInterface:: |
|
ProcessorPluginBase:: |
public | function | ||
ProcessorPluginBase:: |
public | function |
Checks if the facet is supported by this widget. Overrides ProcessorInterface:: |
6 |
ProcessorPluginBase:: |
public | function |
Checks whether this processor implements a particular stage. Overrides ProcessorInterface:: |
|
ProcessorPluginBase:: |
public | function |
Validates a configuration form for this processor. Overrides ProcessorInterface:: |
2 |
QueryString:: |
protected | property | The event dispatcher. | |
QueryString:: |
protected | property | A string of how to represent the facet in the url. | |
QueryString:: |
public | function |
Adds urls to the results. Overrides UrlProcessorInterface:: |
|
QueryString:: |
public static | function |
Creates an instance of the plugin. Overrides UrlProcessorPluginBase:: |
|
QueryString:: |
protected | function | Gets the facet id from the url alias & facet source id. | |
QueryString:: |
protected | function | Gets a request object based on the facet source path. | |
QueryString:: |
protected | function | Gets the url alias from the facet id & facet source id. | |
QueryString:: |
protected | function | Gets the URL object for a request. | |
QueryString:: |
protected | function | Initializes the active filters from the request query. | |
QueryString:: |
public | function |
Constructs a new instance of the class. Overrides UrlProcessorPluginBase:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
UrlProcessorPluginBase:: |
protected | property | An array of active filters. | |
UrlProcessorPluginBase:: |
protected | property | The entity type manager. | |
UrlProcessorPluginBase:: |
protected | property | The query string variable. | |
UrlProcessorPluginBase:: |
protected | property | The clone of the current request object. | |
UrlProcessorPluginBase:: |
protected | property | The url separator variable. | 1 |
UrlProcessorPluginBase:: |
public | function |
Returns the active filters. Overrides UrlProcessorInterface:: |
|
UrlProcessorPluginBase:: |
public | function |
Returns the filter key. Overrides UrlProcessorInterface:: |
|
UrlProcessorPluginBase:: |
public | function |
Returns the url separator. Overrides UrlProcessorInterface:: |
|
UrlProcessorPluginBase:: |
public | function |
Set active filters. Overrides UrlProcessorInterface:: |
|
UrlProcessorPluginBase:: |
public | function |
Sets active items. Overrides UrlProcessorInterface:: |