Skip to content
Commits on Source (20)
...@@ -4,9 +4,7 @@ CONTENTS OF THIS FILE ...@@ -4,9 +4,7 @@ CONTENTS OF THIS FILE
* Requirements * Requirements
* Installation * Installation
* Configuration * Configuration
* Troubleshooting
* FAQ * FAQ
* Maintainers
INTRODUCTION INTRODUCTION
------------ ------------
...@@ -14,8 +12,9 @@ Todo ...@@ -14,8 +12,9 @@ Todo
REQUIREMENTS REQUIREMENTS
------------ ------------
No other modules required, though the module is useless without an implementing No other modules required, we're supporting drupal core as a source for creating
module like search api. facets. Though we recommend using Search API, as that integration is better
tested.
INSTALLATION INSTALLATION
------------ ------------
...@@ -25,17 +24,14 @@ INSTALLATION ...@@ -25,17 +24,14 @@ INSTALLATION
CONFIGURATION CONFIGURATION
------------- -------------
Todo Before adding a facet, there should be a facet source. Facet sources can be:
- Drupal core's search.
- A view based on a Search API index with a page display.
- A page from the search_api_page module.
TROUBLESHOOTING After adding one of those, you can add a facet on the facets configuration page:
--------------- /admin/config/search/facets
Todo
FAQ FAQ
--- ---
Todo Todo
MAINTAINERS
-----------
Current maintainers:
* Todo
...@@ -26,6 +26,12 @@ facets.facet.*: ...@@ -26,6 +26,12 @@ facets.facet.*:
query_type_name: query_type_name:
type: string type: string
label: 'Query Type Name' label: 'Query Type Name'
query_operator:
type: string
label: 'Query Operator'
exclude:
type: boolean
label: 'Exclude'
widget: widget:
type: string type: string
label: 'Widget identifier' label: 'Widget identifier'
...@@ -75,3 +81,11 @@ facets.facet.*: ...@@ -75,3 +81,11 @@ facets.facet.*:
sequence: sequence:
type: plugin.plugin_configuration.facets_facet_options.[%key] type: plugin.plugin_configuration.facets_facet_options.[%key]
label: 'Facet plugin options' label: 'Facet plugin options'
condition.plugin.other_facet:
type: condition.plugin
mapping:
facet_value:
type: string
facets:
type: string
...@@ -11,7 +11,7 @@ facets.facet_source.*: ...@@ -11,7 +11,7 @@ facets.facet_source.*:
name: name:
type: label type: label
label: Name' label: Name'
filterKey: filter_key:
type: string type: string
label: 'Filter key' label: 'Filter key'
url_processor: url_processor:
......
...@@ -40,3 +40,9 @@ plugin.plugin_configuration.facets_processor.count_limit: ...@@ -40,3 +40,9 @@ plugin.plugin_configuration.facets_processor.count_limit:
maximum_items: maximum_items:
type: integer type: integer
label: 'Maximum amount of items to show.' label: 'Maximum amount of items to show.'
plugin.plugin_configuration.facets_processor.url_processor_handler:
type: config_object
plugin.plugin_configuration.facets_processor.hide_non_narrowing_result_processor:
type: config_object
...@@ -80,8 +80,8 @@ class FacetsQuery extends SearchQuery { ...@@ -80,8 +80,8 @@ class FacetsQuery extends SearchQuery {
// For complex search queries, add the LIKE conditions. // For complex search queries, add the LIKE conditions.
/*if (!$this->simple) { /*if (!$this->simple) {
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); $this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
$this->condition($this->conditions); $this->condition($this->conditions);
}*/ }*/
// Add conditions to query. // Add conditions to query.
......
...@@ -9,15 +9,17 @@ namespace Drupal\core_search_facets\Plugin\Search; ...@@ -9,15 +9,17 @@ namespace Drupal\core_search_facets\Plugin\Search;
use Drupal\Core\Config\Config; use Drupal\Core\Config\Config;
use Drupal\Core\Database\Driver\mysql\Connection; use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\facets\FacetSource\FacetSourcePluginManager;
use Drupal\node\Plugin\Search\NodeSearch; use Drupal\node\Plugin\Search\NodeSearch;
use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\RendererInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/** /**
* Handles searching for node entities using the Search module index. * Handles searching for node entities using the Search module index.
...@@ -34,13 +36,13 @@ class NodeSearchFacets extends NodeSearch { ...@@ -34,13 +36,13 @@ class NodeSearchFacets extends NodeSearch {
$plugin_id, $plugin_id,
$plugin_definition, $plugin_definition,
Connection $database, Connection $database,
EntityManagerInterface $entity_manager, EntityTypeManagerInterface $entity_manager,
ModuleHandlerInterface $module_handler, ModuleHandlerInterface $module_handler,
Config $search_settings, Config $search_settings,
LanguageManagerInterface $language_manager, LanguageManagerInterface $language_manager,
RendererInterface $renderer, RendererInterface $renderer,
$facet_source_plugin_manager, FacetSourcePluginManager $facet_source_plugin_manager,
$request_stack, RequestStack $request_stack,
AccountInterface $account = NULL) { AccountInterface $account = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $database, $entity_manager, $module_handler, $search_settings, $language_manager, $renderer, $account); parent::__construct($configuration, $plugin_id, $plugin_definition, $database, $entity_manager, $module_handler, $search_settings, $language_manager, $renderer, $account);
...@@ -60,7 +62,7 @@ class NodeSearchFacets extends NodeSearch { ...@@ -60,7 +62,7 @@ class NodeSearchFacets extends NodeSearch {
$plugin_id, $plugin_id,
$plugin_definition, $plugin_definition,
$container->get('database'), $container->get('database'),
$container->get('entity.manager'), $container->get('entity_type.manager'),
$container->get('module_handler'), $container->get('module_handler'),
$container->get('config.factory')->get('search.settings'), $container->get('config.factory')->get('search.settings'),
$container->get('language_manager'), $container->get('language_manager'),
...@@ -96,6 +98,7 @@ class NodeSearchFacets extends NodeSearch { ...@@ -96,6 +98,7 @@ class NodeSearchFacets extends NodeSearch {
'#access' => $this->account && $this->account->hasPermission('use advanced search'), '#access' => $this->account && $this->account->hasPermission('use advanced search'),
'#open' => $used_advanced, '#open' => $used_advanced,
); );
$form['advanced']['keywords-fieldset'] = array( $form['advanced']['keywords-fieldset'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Keywords'), '#title' => t('Keywords'),
......
...@@ -13,10 +13,11 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; ...@@ -13,10 +13,11 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface; use Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface;
use Drupal\facets\FacetInterface; use Drupal\facets\FacetInterface;
use Drupal\facets\FacetSource\FacetSourcePluginBase; use Drupal\facets\FacetSource\FacetSourcePluginBase;
use Drupal\facets\FacetSource\FacetSourcePluginInterface; use Drupal\facets\QueryType\QueryTypePluginManager;
use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldStorageConfig;
use Drupal\search\SearchPageInterface; use Drupal\search\SearchPageInterface;
use Drupal\search\SearchPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
...@@ -54,6 +55,11 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -54,6 +55,11 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
*/ */
protected $configFactory; protected $configFactory;
/**
* The plugin manager for core search plugins.
*
* @var \Drupal\search\SearchPluginManager
*/
protected $searchManager; protected $searchManager;
/** /**
...@@ -64,7 +70,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -64,7 +70,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function __construct(array $configuration, $plugin_id, array $plugin_definition, $query_type_plugin_manager, $search_manager, RequestStack $request_stack) { public function __construct(array $configuration, $plugin_id, array $plugin_definition, QueryTypePluginManager $query_type_plugin_manager, SearchPluginManager $search_manager, RequestStack $request_stack) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager); parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager);
$this->searchManager = $search_manager; $this->searchManager = $search_manager;
$this->setSearchKeys($request_stack->getMasterRequest()->query->get('keys')); $this->setSearchKeys($request_stack->getMasterRequest()->query->get('keys'));
...@@ -112,7 +118,6 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -112,7 +118,6 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
// Get the Facet Specific Query Type so we can process the results // Get the Facet Specific Query Type so we can process the results
// using the build() function of the query type. // using the build() function of the query type.
/** @var \Drupal\facets\Entity\Facet $facet **/
$query_type = $this->queryTypePluginManager->createInstance($facet->getQueryType(), $configuration); $query_type = $this->queryTypePluginManager->createInstance($facet->getQueryType(), $configuration);
$query_type->build(); $query_type->build();
} }
...@@ -143,7 +148,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -143,7 +148,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* @return array * @return array
* An array of query types. * An array of query types.
*/ */
public function getQueryTypesForFieldType($field_type) { protected function getQueryTypesForFieldType($field_type) {
$query_types = []; $query_types = [];
switch ($field_type) { switch ($field_type) {
case 'type': case 'type':
...@@ -178,7 +183,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -178,7 +183,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet, FacetSourcePluginInterface $facet_source) { public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['field_identifier'] = [ $form['field_identifier'] = [
'#type' => 'select', '#type' => 'select',
...@@ -186,7 +191,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -186,7 +191,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
'#title' => $this->t('Facet field'), '#title' => $this->t('Facet field'),
'#description' => $this->t('Choose the indexed field.'), '#description' => $this->t('Choose the indexed field.'),
'#required' => TRUE, '#required' => TRUE,
'#default_value' => $facet->getFieldIdentifier(), '#default_value' => $this->facet->getFieldIdentifier(),
]; ];
return $form; return $form;
...@@ -204,10 +209,13 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -204,10 +209,13 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
// Get the current field instances and detect if the field type is allowed. // Get the current field instances and detect if the field type is allowed.
$fields = FieldConfig::loadMultiple(); $fields = FieldConfig::loadMultiple();
/** @var \Drupal\Field\FieldConfigInterface $field */
foreach ($fields as $field) { foreach ($fields as $field) {
// Verify if the target type is allowed for entity reference fields, // Verify if the target type is allowed for entity reference fields,
// otherwise verify the field type(i.e. integer, float...). // otherwise verify the field type(i.e. integer, float...).
if (in_array($field->getFieldStorageDefinition()->getSetting('target_type'), $allowed_field_types) || in_array($field->getFieldStorageDefinition()->getType(), $allowed_field_types)) { $target_is_allowed = in_array($field->getFieldStorageDefinition()->getSetting('target_type'), $allowed_field_types);
$field_is_allowed = in_array($field->getFieldStorageDefinition()->getType(), $allowed_field_types);
if ($target_is_allowed || $field_is_allowed) {
/** @var \Drupal\field\Entity\FieldConfig $field */ /** @var \Drupal\field\Entity\FieldConfig $field */
if (!array_key_exists($field->getName(), $facet_fields)) { if (!array_key_exists($field->getName(), $facet_fields)) {
$facet_fields[$field->getName()] = $this->t('@label', ['@label' => $field->getLabel()]); $facet_fields[$field->getName()] = $this->t('@label', ['@label' => $field->getLabel()]);
...@@ -222,6 +230,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -222,6 +230,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* Getter for default node fields. * Getter for default node fields.
* *
* @return array * @return array
* An array containing the default fields enabled on a node.
*/ */
protected function getDefaultFields() { protected function getDefaultFields() {
return [ return [
...@@ -235,14 +244,14 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -235,14 +244,14 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFacetQueryExtender() { public function getFacetQueryExtender() {
if (!$this->facetQueryExtender) { if (!$this->facetQueryExtender) {
$this->facetQueryExtender = db_select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\core_search_facets\FacetsQuery'); $this->facetQueryExtender = db_select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\core_search_facets\FacetsQuery');
$this->facetQueryExtender->join('node_field_data', 'n', 'n.nid = i.sid'); $this->facetQueryExtender->join('node_field_data', 'n', 'n.nid = i.sid');
$this->facetQueryExtender $this->facetQueryExtender
// ->condition('n.status', 1). // ->condition('n.status', 1).
->addTag('node_access') ->addTag('node_access')
->searchExpression($this->keys, 'node_search'); ->searchExpression($this->keys, 'node_search');
} }
return $this->facetQueryExtender; return $this->facetQueryExtender;
} }
...@@ -254,10 +263,10 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea ...@@ -254,10 +263,10 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
$field_name = $facet->getFieldIdentifier(); $field_name = $facet->getFieldIdentifier();
$default_fields = $this->getDefaultFields(); $default_fields = $this->getDefaultFields();
if (array_key_exists($facet->getFieldIdentifier(), $default_fields)) { if (array_key_exists($facet->getFieldIdentifier(), $default_fields)) {
// We add the language code of the indexed item to the result of the query. // We add the language code of the indexed item to the result of the
// So in this case we need to use the search_index table alias (i) for the // query. So in this case we need to use the search_index table alias (i)
// langcode field. Otherwise we will have same nid for multiple languages // for the langcode field. Otherwise we will have same nid for multiple
// as result. For more details see NodeSearch::findResults(). // languages as result. For more details see NodeSearch::findResults().
// @TODO review if I can refactor this. // @TODO review if I can refactor this.
$table_alias = $facet->getFieldIdentifier() == 'langcode' ? 'i' : 'n'; $table_alias = $facet->getFieldIdentifier() == 'langcode' ? 'i' : 'n';
$query_info = [ $query_info = [
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
namespace Drupal\core_search_facets\Plugin\facets\facet_source; namespace Drupal\core_search_facets\Plugin\facets\facet_source;
use Drupal\Component\Plugin\PluginBase; use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\facets\FacetSource\FacetSourceDeriverBase; use Drupal\facets\FacetSource\FacetSourceDeriverBase;
use Drupal\search\SearchPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
...@@ -18,12 +20,24 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -18,12 +20,24 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/ */
class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase { class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
/**
* The plugin manager for core search plugins.
*
* @var \Drupal\search\SearchPluginManager
*/
protected $searchManager; protected $searchManager;
/** /**
* Create an instance of the deriver. * Creates an instance of the deriver.
*
* @param string $base_plugin_id
* The plugin ID.
* @param \Drupal\search\SearchPluginManager $search_manager
* The plugin manager for core search plugins.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager.
*/ */
public function __construct(ContainerInterface $container, $base_plugin_id, $search_manager, $entity_type_manager) { public function __construct($base_plugin_id, SearchPluginManager $search_manager, EntityTypeManagerInterface $entity_type_manager) {
$this->searchManager = $search_manager; $this->searchManager = $search_manager;
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
} }
...@@ -33,11 +47,10 @@ class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase { ...@@ -33,11 +47,10 @@ class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
*/ */
public static function create(ContainerInterface $container, $base_plugin_id) { public static function create(ContainerInterface $container, $base_plugin_id) {
return new static( return new static(
$container,
$base_plugin_id, $base_plugin_id,
$container->get('plugin.manager.search'), $container->get('plugin.manager.search'),
$container->get('entity_type.manager') $container->get('entity_type.manager')
); );
} }
/** /**
......
...@@ -21,7 +21,7 @@ use Drupal\facets\Result\Result; ...@@ -21,7 +21,7 @@ use Drupal\facets\Result\Result;
class CoreNodeSearchString extends QueryTypePluginBase { class CoreNodeSearchString extends QueryTypePluginBase {
/** /**
* Holds the backend's native query object. * The backend's native query object.
* *
* @var \Drupal\search_api\Query\QueryInterface * @var \Drupal\search_api\Query\QueryInterface
*/ */
...@@ -31,7 +31,6 @@ class CoreNodeSearchString extends QueryTypePluginBase { ...@@ -31,7 +31,6 @@ class CoreNodeSearchString extends QueryTypePluginBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function execute() { public function execute() {
/** @var \Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface $facet_source */ /** @var \Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface $facet_source */
$facet_source = $this->facet->getFacetSource(); $facet_source = $this->facet->getFacetSource();
$query_info = $facet_source->getQueryInfo($this->facet); $query_info = $facet_source->getQueryInfo($this->facet);
......
<?php
/**
* @file
* Contains Drupal\facets_query_processor\Plugin\facets\url_processor\DummyQuery.
*/
namespace Drupal\facets_query_processor\Plugin\facets\url_processor;
use Drupal\Core\Url;
use Drupal\facets\FacetInterface;
use Drupal\facets\UrlProcessor\UrlProcessorPluginBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Query string URL processor.
*
* @FacetsUrlProcessor(
* id = "dummy_query",
* label = @Translation("Dummy query"),
* description = @Translation("Dummy for testing.")
* )
*/
class DummyQuery extends UrlProcessorPluginBase {
/**
* A string that separates the filters in the query string.
*/
const SEPARATOR = '||';
/**
* A string of how to represent the facet in the url.
*
* @var string
*/
protected $url_alias;
/**
* An array of active filters.
*
* @var string[]
* An array containing the active filters
*/
protected $activeFilters = [];
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $request);
$this->initializeActiveFilters();
}
/**
* {@inheritdoc}
*/
public function buildUrls(FacetInterface $facet, array $results) {
// Create links for all the values.
// First get the current list of get parameters.
$get_params = $this->request->query;
// Set the url alias from the the facet object.
$this->url_alias = $facet->getUrlAlias();
// No results are found for this facet, so don't try to create urls.
if (empty($results)) {
return [];
}
/** @var \Drupal\facets\Result\ResultInterface $result */
foreach ($results as &$result) {
$filter_string = $this->url_alias . self::SEPARATOR . $result->getRawValue();
$result_get_params = clone $get_params;
$filter_params = $result_get_params->get($this->filterKey, [], TRUE);
// 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 the value is not active, add the filter string.
else {
$filter_params[] = $filter_string;
}
$result_get_params->set($this->filterKey, $filter_params);
$request = $this->request;
if ($facet->getFacetSource()->getPath()) {
$request = Request::create('/' . $facet->getFacetSource()->getPath());
}
$url = Url::createFromRequest($request);
$url->setOption('query', $result_get_params->all());
$result->setUrl($url);
}
return $results;
}
/**
* {@inheritdoc}
*/
public function setActiveItems(FacetInterface $facet) {
// Set the url alias from the the facet object.
$this->url_alias = $facet->getUrlAlias();
// Get the filter key of the facet.
if (isset($this->activeFilters[$this->url_alias])) {
foreach ($this->activeFilters[$this->url_alias] as $value) {
$facet->setActiveItem(trim($value, '"'));
}
}
}
/**
* Initialize the active filters.
*
* Get all the filters that are active. This method only get's all the
* filters but doesn't assign them to facets. In the processFacet method the
* active values for a specific facet are added to the facet.
*/
protected function initializeActiveFilters() {
$url_parameters = $this->request->query;
// Get the active facet parameters.
$active_params = $url_parameters->get($this->filterKey, array(), TRUE);
// Explode the active params on the separator.
foreach ($active_params as $param) {
list($key, $value) = explode(self::SEPARATOR, $param);
if (!isset($this->activeFilters[$key])) {
$this->activeFilters[$key] = [$value];
}
else {
$this->activeFilters[$key][] = $value;
}
}
}
}
...@@ -9,7 +9,6 @@ namespace Drupal\core_search_facets\Tests; ...@@ -9,7 +9,6 @@ namespace Drupal\core_search_facets\Tests;
use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field_ui\Tests\FieldUiTestTrait;
/** /**
* Tests integration of hooks. * Tests integration of hooks.
...@@ -25,7 +24,7 @@ class HooksTest extends WebTestBase { ...@@ -25,7 +24,7 @@ class HooksTest extends WebTestBase {
'node', 'node',
'search', 'search',
'core_search_facets_test_hooks', 'core_search_facets_test_hooks',
'field' 'field',
]; ];
/** /**
...@@ -34,8 +33,6 @@ class HooksTest extends WebTestBase { ...@@ -34,8 +33,6 @@ class HooksTest extends WebTestBase {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->drupalCreateContentType(['type' => 'page']);
// Create a field of type float. // Create a field of type float.
FieldStorageConfig::create( FieldStorageConfig::create(
[ [
...@@ -51,7 +48,7 @@ class HooksTest extends WebTestBase { ...@@ -51,7 +48,7 @@ class HooksTest extends WebTestBase {
'field_name' => 'float', 'field_name' => 'float',
'entity_type' => 'node', 'entity_type' => 'node',
'bundle' => 'page', 'bundle' => 'page',
'label' => 'Float Field Label' 'label' => 'Float Field Label',
] ]
)->save(); )->save();
...@@ -64,7 +61,6 @@ class HooksTest extends WebTestBase { ...@@ -64,7 +61,6 @@ class HooksTest extends WebTestBase {
*/ */
public function testHooks() { public function testHooks() {
// Verify that hook_facets_core_allowed_field_types was triggered. // Verify that hook_facets_core_allowed_field_types was triggered.
$facet_add_page = 'admin/config/search/facets/add-facet'; $facet_add_page = 'admin/config/search/facets/add-facet';
$this->drupalGet($facet_add_page); $this->drupalGet($facet_add_page);
$this->assertResponse(200); $this->assertResponse(200);
......
<?php
/**
* @file
* Contains \Drupal\core_search_facets\Tests\IntegrationTest.
*/
namespace Drupal\core_search_facets\Tests;
use Drupal\core_search_facets\Tests\WebTestBase as CoreSearchFacetsWebTestBase;
/**
* Tests the admin UI with the core search facet source.
*
* @group core_search_facets
*/
class IntegrationTest extends CoreSearchFacetsWebTestBase {
/**
* The block entities used by this test.
*
* @var \Drupal\block\BlockInterface[]
*/
protected $blocks;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
// Index the content.
\Drupal::service('plugin.manager.search')->createInstance('node_search')->updateIndex();
search_update_totals();
// Make absolutely sure the ::$blocks variable doesn't pass information
// along between tests.
$this->blocks = NULL;
}
/**
* Tests various operations via the Facets' admin UI.
*/
public function testFramework() {
$facet_name = "Test Facet name";
$facet_id = 'test_facet_name';
// Check if the overview is empty.
$this->checkEmptyOverview();
// Add a new facet and edit it.
$this->addFacet($facet_name);
$this->editFacet($facet_name);
// Create and place a block for "Test Facet name" facet.
$this->createFacetBlock($facet_id);
// Verify that the facet results are correct.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertLink('page');
$this->assertLink('article');
// Verify that facet blocks appear as expected.
$this->assertFacetBlocksAppear();
$this->setShowAmountOfResults($facet_name, TRUE);
// Verify that the number of results per item.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertLink('page (10)');
$this->assertLink('article (10)');
// Do not show the block on empty behaviors.
// Truncate the search_index table because, for the moment, we don't have
// the possibility to clear the index from the API.
// See https://www.drupal.org/node/326062
\Drupal::database()->truncate('search_index')->execute();
// Verify that no facet blocks appear. Empty behavior "None" is selected by
// default.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertNoFacetBlocksAppear();
// Verify that the "empty_text" appears as expected.
$this->setEmptyBehaviorFacetText($facet_name);
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertRaw('block-test-facet-name');
$this->assertRaw('No results found for this block!');
// Delete the block.
$this->deleteBlock($facet_id);
// Delete the facet and make sure the overview is empty again.
$this->deleteUnusedFacet($facet_name);
$this->checkEmptyOverview();
}
/**
* Configures the possibility to show the amount of results for facet blocks.
*
* @param string $facet_name
* The name of the facet.
* @param bool|TRUE $show
* Boolean to determine if we want to show the amount of results.
*/
protected function setShowAmountOfResults($facet_name, $show = TRUE) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
// Configure the text for empty results behavior.
$edit = [
'widget_configs[show_numbers]' => $show,
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Deletes a facet block by id.
*
* @param string $id
* The id of the block.
*/
protected function deleteBlock($id) {
$this->drupalGet('admin/structure/block/manage/' . $this->blocks[$id]->id(), array('query' => array('destination' => 'admin')));
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, array(), t('Delete'));
$this->assertRaw(t('The block %name has been deleted.', array('%name' => $this->blocks[$id]->label())));
}
/**
* Asserts that a facet block does not appear.
*/
protected function assertNoFacetBlocksAppear() {
foreach ($this->blocks as $block) {
$this->assertNoBlockAppears($block);
}
}
/**
* Asserts that a facet block appears.
*/
protected function assertFacetBlocksAppear() {
foreach ($this->blocks as $block) {
$this->assertBlockAppears($block);
}
}
/**
* Creates a facet block by id.
*
* @param string $id
* The id of the block.
*/
protected function createFacetBlock($id) {
$block = [
'plugin_id' => 'facet_block:' . $id,
'settings' => [
'region' => 'footer',
'id' => str_replace('_', '-', $id),
],
];
$this->blocks[$id] = $this->drupalPlaceBlock($block['plugin_id'], $block['settings']);
}
/**
* Configures empty behavior option to show a text on empty results.
*
* @param string $facet_name
* The name of the facet.
*/
protected function setEmptyBehaviorFacetText($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
// Configure the text for empty results behavior.
$edit = [
'facet_settings[empty_behavior]' => 'text',
'facet_settings[empty_behavior_container][empty_behavior_text][value]' => 'No results found for this block!',
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Configures a facet to only be visible when accessing to the facet source.
*
* @param string $facet_name
* The name of the facet.
*/
protected function setOptionShowOnlyWhenFacetSourceVisible($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
$edit = [
'facet_settings[only_visible_when_facet_source_is_visible]' => TRUE,
'widget' => 'links',
'widget_configs[show_numbers]' => '0',
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Get the facet overview page and make sure the overview is empty.
*/
protected function checkEmptyOverview() {
$facet_overview = '/admin/config/search/facets';
$this->drupalGet($facet_overview);
$this->assertResponse(200);
// The list overview has Field: field_name as description. This tests on the
// absence of that.
$this->assertNoText('Field:');
}
/**
* Tests adding a facet trough the interface.
*
* @param string $facet_name
* The name of the facet.
*/
protected function addFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
// Go to the Add facet page and make sure that returns a 200.
$facet_add_page = '/admin/config/search/facets/add-facet';
$this->drupalGet($facet_add_page);
$this->assertResponse(200);
$form_values = [
'name' => '',
'id' => $facet_id,
'status' => 1,
'url_alias' => $facet_id,
];
// Try filling out the form, but without having filled in a name for the
// facet to test for form errors.
$this->drupalPostForm($facet_add_page, $form_values, $this->t('Save'));
$this->assertText($this->t('Facet name field is required.'));
$this->assertText($this->t('Facet source field is required.'));
// Make sure that when filling out the name, the form error disappears.
$form_values['name'] = $facet_name;
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->assertNoText($this->t('Facet name field is required.'));
// Configure the facet source by selecting one of the search api views.
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'core_node_search:node_search'], $this->t('Configure facet source'));
// The facet field is still required.
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->assertText($this->t('Facet field field is required.'));
// Fill in all fields and make sure the 'field is required' message is no
// longer shown.
$facet_source_form = [
'facet_source_configs[core_node_search:node_search][field_identifier]' => 'type',
];
$this->drupalPostForm(NULL, $form_values + $facet_source_form, $this->t('Save'));
$this->assertNoText('field is required.');
// Make sure that the redirection to the display page is correct.
$this->assertRaw(t('Facet %name has been created.', ['%name' => $facet_name]));
$this->assertUrl('admin/config/search/facets/' . $facet_id . '/display');
$this->drupalGet('admin/config/search/facets');
}
/**
* Tests editing of a facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
public function editFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_edit_page = '/admin/config/search/facets/' . $facet_id . '/edit';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_edit_page);
$this->assertResponse(200);
$this->assertRaw($this->t('Edit facet @facet', ['@facet' => $facet_name]));
// Change the facet name to add in "-2" to test editing of a facet works.
$form_values = ['name' => $facet_name . ' - 2'];
$this->drupalPostForm($facet_edit_page, $form_values, $this->t('Save'));
// Make sure that the redirection back to the overview was successful and
// the edited facet is shown on the overview page.
$this->assertRaw(t('Facet %name has been updated.', ['%name' => $facet_name . ' - 2']));
// Make sure the "-2" suffix is still on the facet when editing a facet.
$this->drupalGet($facet_edit_page);
$this->assertRaw($this->t('Edit facet @facet', ['@facet' => $facet_name . ' - 2']));
// Edit the form and change the facet's name back to the initial name.
$form_values = ['name' => $facet_name];
$this->drupalPostForm($facet_edit_page, $form_values, $this->t('Save'));
// Make sure that the redirection back to the overview was successful and
// the edited facet is shown on the overview page.
$this->assertRaw(t('Facet %name has been updated.', ['%name' => $facet_name]));
}
/**
* This deletes an unused facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
protected function deleteUsedFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
// Check that the facet by testing for the message and the absence of the
// facet name on the overview.
$this->assertRaw($this->t('The facet is currently used in a block and thus can\'t be removed. Remove the block first.'));
}
/**
* This deletes a facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
protected function deleteUnusedFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
$this->assertText($this->t('This action cannot be undone'));
// Actually submit the confirmation form.
$this->drupalPostForm(NULL, [], $this->t('Delete'));
// Check that the facet by testing for the message and the absence of the
// facet name on the overview.
$this->assertRaw($this->t('The facet %facet has been deleted.', ['%facet' => $facet_name]));
// Refresh the page because on the previous page the $facet_name is still
// visible (in the message).
$facet_overview = '/admin/config/search/facets';
$this->drupalGet($facet_overview);
$this->assertResponse(200);
$this->assertNoText($facet_name);
}
/**
* Convert facet name to machine name.
*
* @param string $facet_name
* The name of the facet.
*
* @return string
* The facet name changed to a machine name.
*/
protected function convertNameToMachineName($facet_name) {
return preg_replace('@[^a-zA-Z0-9_]+@', '_', strtolower($facet_name));
}
/**
* Go to the Delete Facet Page using the facet name.
*
* @param string $facet_name
* The name of the facet.
*/
protected function goToDeleteFacetPage($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
}
}
...@@ -17,21 +17,15 @@ abstract class WebTestBase extends SimpletestWebTestBase { ...@@ -17,21 +17,15 @@ abstract class WebTestBase extends SimpletestWebTestBase {
use StringTranslationTrait; use StringTranslationTrait;
/**
* Exempt from strict schema checking.
*
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
*
* @var bool
*/
protected $strictConfigSchema = FALSE;
/** /**
* Modules to enable for this test. * Modules to enable for this test.
* *
* @var string[] * @var string[]
*/ */
public static $modules = [ public static $modules = [
'field',
'search',
'entity_test',
'views', 'views',
'node', 'node',
'facets', 'facets',
...@@ -60,19 +54,34 @@ abstract class WebTestBase extends SimpletestWebTestBase { ...@@ -60,19 +54,34 @@ abstract class WebTestBase extends SimpletestWebTestBase {
*/ */
protected $anonymousUser; protected $anonymousUser;
/**
* A search index ID.
*
* @var string
*/
protected $indexId = 'database_search_index';
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
// Create content types.
$this->drupalCreateContentType(['type' => 'page']);
$this->drupalCreateContentType(['type' => 'article']);
// Adding 10 pages.
for ($i = 0; $i < 10; $i++) {
$this->drupalCreateNode(array(
'title' => 'foo bar' . $i,
'body' => 'test page' . $i,
'type' => 'page',
));
}
// Adding 10 articles.
for ($i = 0; $i < 10; $i++) {
$this->drupalCreateNode(array(
'title' => 'foo baz' . $i,
'body' => 'test article' . $i,
'type' => 'article',
));
}
// Create the users used for the tests. // Create the users used for the tests.
$this->adminUser = $this->drupalCreateUser([ $this->adminUser = $this->drupalCreateUser([
'administer search', 'administer search',
...@@ -84,10 +93,6 @@ abstract class WebTestBase extends SimpletestWebTestBase { ...@@ -84,10 +93,6 @@ abstract class WebTestBase extends SimpletestWebTestBase {
'administer blocks', 'administer blocks',
'search content', 'search content',
]); ]);
$this->unauthorizedUser = $this->drupalCreateUser(['access administration pages']);
$this->anonymousUser = $this->drupalCreateUser();
} }
} }
...@@ -4,3 +4,6 @@ description: 'Faceted search interfaces that can be used on Search API searchers ...@@ -4,3 +4,6 @@ description: 'Faceted search interfaces that can be used on Search API searchers
core: 8.x core: 8.x
package: Search package: Search
configure: facets.overview configure: facets.overview
test_dependencies:
- search_api:search_api
- drupal:views
...@@ -7,12 +7,10 @@ ...@@ -7,12 +7,10 @@
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\search_api\Query\QueryInterface; use Drupal\search_api\Query\QueryInterface;
use Drupal\facets\FacetInterface;
/** /**
* Implements hook_help(). * Implements hook_help().
*/ */
function facets_help($route_name, RouteMatchInterface $route_match) { function facets_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) { switch ($route_name) {
// Main module help for the facets module. // Main module help for the facets module.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/** /**
* @file * @file
* Contains \Drupal\facets\Annotation\FacetsFacet. * Contains \Drupal\facets\Annotation\FacetsFacetSource.
*/ */
namespace Drupal\facets\Annotation; namespace Drupal\facets\Annotation;
...@@ -29,7 +29,7 @@ class FacetsFacetSource extends Plugin { ...@@ -29,7 +29,7 @@ class FacetsFacetSource extends Plugin {
public $id; public $id;
/** /**
* The human-readable name of the facet soruce plugin. * The human-readable name of the facet source plugin.
* *
* @ingroup plugin_translatable * @ingroup plugin_translatable
* *
......
...@@ -43,7 +43,8 @@ use Drupal\facets\FacetInterface; ...@@ -43,7 +43,8 @@ use Drupal\facets\FacetInterface;
* "facet_source_id", * "facet_source_id",
* "widget", * "widget",
* "widget_configs", * "widget_configs",
* "options", * "query_operator",
* "exclude",
* "only_visible_when_facet_source_is_visible", * "only_visible_when_facet_source_is_visible",
* "processor_configs", * "processor_configs",
* "empty_behavior", * "empty_behavior",
...@@ -98,18 +99,23 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -98,18 +99,23 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/** /**
* Configuration for the widget. This is a key-value stored array. * Configuration for the widget. This is a key-value stored array.
* *
* @var string * @var array
*/ */
protected $widget_configs; protected $widget_configs = [];
/** /**
* An array of options configuring this facet. * The operator to hand over to the query, currently AND | OR.
* *
* @var array * @var string
*/
protected $query_operator;
/**
* A boolean flag indicating if search should exclude selected facets.
* *
* @see getOptions() * @var bool
*/ */
protected $options = array(); protected $exclude;
/** /**
* The field identifier. * The field identifier.
...@@ -162,6 +168,18 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -162,6 +168,18 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/ */
protected $results = []; protected $results = [];
/**
* The results.
*
* @var \Drupal\facets\Result\ResultInterface[]
*/
protected $unfiltered_results = [];
/**
* An array of active values.
*
* @var string[]
*/
protected $active_values = []; protected $active_values = [];
/** /**
...@@ -185,14 +203,14 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -185,14 +203,14 @@ class Facet extends ConfigEntityBase implements FacetInterface {
* *
* @var array * @var array
*/ */
protected $processor_configs; protected $processor_configs = [];
/** /**
* Additional facet configurations. * Additional facet configurations.
* *
* @var array * @var array
*/ */
protected $facet_configs; protected $facet_configs = [];
/** /**
* Is the facet only visible when the facet source is only visible. * Is the facet only visible when the facet source is only visible.
...@@ -234,7 +252,7 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -234,7 +252,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
} }
/** /**
* Gets the widget plugin manager. * Returns the widget plugin manager.
* *
* @return \Drupal\facets\Widget\WidgetPluginManager * @return \Drupal\facets\Widget\WidgetPluginManager
* The widget plugin manager. * The widget plugin manager.
...@@ -248,8 +266,9 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -248,8 +266,9 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function id() { protected function urlRouteParameters($rel) {
return $this->id; $parameters = parent::urlRouteParameters($rel);
return $parameters;
} }
/** /**
...@@ -267,6 +286,13 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -267,6 +286,13 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this; return $this;
} }
/**
* {@inheritdoc}
*/
public function getQueryTypes() {
return $this->query_type_name;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -274,6 +300,48 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -274,6 +300,48 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->widget; return $this->widget;
} }
/**
* Retrieves all processors supported by this facet.
*
* @return \Drupal\facets\Processor\ProcessorInterface[]
* The loaded processors, keyed by processor ID.
*/
protected function loadProcessors() {
if (!isset($this->processors)) {
/* @var $processor_plugin_manager \Drupal\facets\Processor\ProcessorPluginManager */
$processor_plugin_manager = \Drupal::service('plugin.manager.facets.processor');
$processor_settings = $this->getProcessorConfigs();
foreach ($processor_plugin_manager->getDefinitions() as $name => $processor_definition) {
if (class_exists($processor_definition['class']) && empty($this->processors[$name])) {
// Create our settings for this processor.
$settings = empty($processor_settings[$name]['settings']) ? [] : $processor_settings[$name]['settings'];
$settings['facet'] = $this;
/* @var $processor \Drupal\facets\Processor\ProcessorInterface */
$processor = $processor_plugin_manager->createInstance($name, $settings);
$this->processors[$name] = $processor;
}
elseif (!class_exists($processor_definition['class'])) {
\Drupal::logger('facets')
->warning('Processor @id specifies a non-existing @class.', array(
'@id' => $name,
'@class' => $processor_definition['class'],
));
}
}
}
return $this->processors;
}
/**
* {@inheritdoc}
*/
public function getProcessorConfigs() {
return !empty($this->processor_configs) ? $this->processor_configs : [];
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -292,56 +360,54 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -292,56 +360,54 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFieldAlias() { public function setQueryOperator($operator = '') {
// For now, create the field alias based on the field identifier. return $this->query_operator = $operator;
$field_alias = preg_replace('/[:\/]+/', '_', $this->field_identifier);
return $field_alias;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setActiveItem($value) { public function getQueryOperator() {
if (!in_array($value, $this->active_values)) { return $this->query_operator ?: 'OR';
$this->active_values[] = $value;
}
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getActiveItems() { public function setExclude($exclude) {
return $this->active_values; return $this->exclude = $exclude;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getOption($name, $default = NULL) { public function getExclude() {
return isset($this->options[$name]) ? $this->options[$name] : $default; return $this->exclude;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getOptions() { public function getFieldAlias() {
return $this->options; // For now, create the field alias based on the field identifier.
$field_alias = preg_replace('/[:\/]+/', '_', $this->field_identifier);
return $field_alias;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setOption($name, $option) { public function setActiveItem($value) {
$this->options[$name] = $option; if (!in_array($value, $this->active_values)) {
return $this; $this->active_values[] = $value;
}
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setOptions(array $options) { public function getActiveItems() {
$this->options = $options; return $this->active_values;
return $this;
} }
/** /**
...@@ -359,13 +425,6 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -359,13 +425,6 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this; return $this;
} }
/**
* {@inheritdoc}
*/
public function getQueryTypes() {
return $this->query_type_name;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -411,7 +470,7 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -411,7 +470,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
if (!$this->facet_source_instance && $this->facet_source_id) { if (!$this->facet_source_instance && $this->facet_source_id) {
/* @var $facet_source_plugin_manager \Drupal\facets\FacetSource\FacetSourcePluginManager */ /* @var $facet_source_plugin_manager \Drupal\facets\FacetSource\FacetSourcePluginManager */
$facet_source_plugin_manager = \Drupal::service('plugin.manager.facets.facet_source'); $facet_source_plugin_manager = \Drupal::service('plugin.manager.facets.facet_source');
$this->facet_source_instance = $facet_source_plugin_manager->createInstance($this->facet_source_id); $this->facet_source_instance = $facet_source_plugin_manager->createInstance($this->facet_source_id, ['facet' => $this]);
} }
return $this->facet_source_instance; return $this->facet_source_instance;
...@@ -458,44 +517,6 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -458,44 +517,6 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->facetSourceConfig; return $this->facetSourceConfig;
} }
/**
* Retrieves all processors supported by this facet.
*
* @return \Drupal\facets\Processor\ProcessorInterface[]
* The loaded processors, keyed by processor ID.
*/
protected function loadProcessors() {
if (!isset($this->processors)) {
/* @var $processor_plugin_manager \Drupal\facets\Processor\ProcessorPluginManager */
$processor_plugin_manager = \Drupal::service('plugin.manager.facets.processor');
foreach ($processor_plugin_manager->getDefinitions() as $processor_id => $processor_definition) {
if (class_exists($processor_definition['class']) && empty($this->processors[$processor_id])) {
$settings = empty($this->processor_configs[$processor_id]['settings']) ? [] : $this->processor_configs[$processor_id]['settings'];
$settings['enabled'] = empty($this->processor_configs[$processor_id]) ? FALSE : TRUE;
$settings['facet'] = $this;
/* @var $processor \Drupal\facets\Processor\ProcessorInterface */
$processor = $processor_plugin_manager->createInstance($processor_id, $settings);
$this->processors[$processor_id] = $processor;
}
elseif (!class_exists($processor_definition['class'])) {
\Drupal::logger('facets')->warning('Processor @id specifies a non-existing @class.', array('@id' => $processor_id, '@class' => $processor_definition['class']));
}
}
}
return $this->processors;
}
/**
* {@inheritdoc}
*/
protected function urlRouteParameters($rel) {
$parameters = parent::urlRouteParameters($rel);
return $parameters;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -519,6 +540,20 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -519,6 +540,20 @@ class Facet extends ConfigEntityBase implements FacetInterface {
} }
} }
/**
* {@inheritdoc}
*/
public function setUnfilteredResults(array $all_results = []) {
$this->unfiltered_results = $all_results;
}
/**
* {@inheritdoc}
*/
public function getUnfilteredResults() {
return $this->unfiltered_results;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -550,7 +585,11 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -550,7 +585,11 @@ class Facet extends ConfigEntityBase implements FacetInterface {
$this->facetSourcePlugins[$name] = $facet_source; $this->facetSourcePlugins[$name] = $facet_source;
} }
elseif (!class_exists($facet_source_definition['class'])) { elseif (!class_exists($facet_source_definition['class'])) {
\Drupal::logger('facets')->warning('Facet Source @id specifies a non-existing @class.', ['@id' => $name, '@class' => $facet_source_definition['class']]); \Drupal::logger('facets')
->warning('Facet Source @id specifies a non-existing @class.', [
'@id' => $name,
'@class' => $facet_source_definition['class'],
]);
} }
} }
} }
...@@ -570,9 +609,9 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -570,9 +609,9 @@ class Facet extends ConfigEntityBase implements FacetInterface {
$processors = $this->loadProcessors(); $processors = $this->loadProcessors();
// Filter processors by status if required. Enabled processors are those // Filter processors by status if required. Enabled processors are those
// which have settings in the "processors" option. // which have settings in the processor_configs.
if ($only_enabled) { if ($only_enabled) {
$processors_settings = !empty($this->processor_configs) ? $this->processor_configs : []; $processors_settings = $this->getProcessorConfigs();
$processors = array_intersect_key($processors, $processors_settings); $processors = array_intersect_key($processors, $processors_settings);
} }
...@@ -584,7 +623,7 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -584,7 +623,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/ */
public function getProcessorsByStage($stage, $only_enabled = TRUE) { public function getProcessorsByStage($stage, $only_enabled = TRUE) {
$processors = $this->loadProcessors(); $processors = $this->loadProcessors();
$processor_settings = $this->processor_configs; $processor_settings = $this->getProcessorConfigs();
$processor_weights = array(); $processor_weights = array();
// Get a list of all processors meeting the criteria (stage and, optionally, // Get a list of all processors meeting the criteria (stage and, optionally,
...@@ -655,7 +694,7 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -655,7 +694,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setEmptyBehavior($empty_behavior) { public function setEmptyBehavior(array $empty_behavior) {
$this->empty_behavior = $empty_behavior; $this->empty_behavior = $empty_behavior;
} }
...@@ -687,6 +726,4 @@ class Facet extends ConfigEntityBase implements FacetInterface { ...@@ -687,6 +726,4 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->facet_configs; return $this->facet_configs;
} }
} }
...@@ -9,7 +9,6 @@ namespace Drupal\facets\Entity; ...@@ -9,7 +9,6 @@ namespace Drupal\facets\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\facets\FacetSourceInterface; use Drupal\facets\FacetSourceInterface;
use Drupal\facets\UrlProcessor\UrlProcessorInterface;
/** /**
* Defines the facet source configuration entity. * Defines the facet source configuration entity.
...@@ -37,8 +36,8 @@ use Drupal\facets\UrlProcessor\UrlProcessorInterface; ...@@ -37,8 +36,8 @@ use Drupal\facets\UrlProcessor\UrlProcessorInterface;
* config_export = { * config_export = {
* "id", * "id",
* "name", * "name",
* "filterKey", * "filter_key",
* "urlProcessor" * "url_processor"
* }, * },
* links = { * links = {
* "canonical" = "/admin/config/search/facets/facet-sources/", * "canonical" = "/admin/config/search/facets/facet-sources/",
...@@ -67,21 +66,14 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface { ...@@ -67,21 +66,14 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface {
* *
* @var string * @var string
*/ */
protected $filterKey; protected $filter_key;
/** /**
* The url processor name. * The url processor name.
* *
* @var string * @var string
*/ */
protected $urlProcessor = 'query_string'; protected $url_processor = 'query_string';
/**
* {@inheritdoc}
*/
public function id() {
return $this->id;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -94,28 +86,28 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface { ...@@ -94,28 +86,28 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setFilterKey($filter_key) { public function setFilterKey($filter_key) {
$this->filterKey = $filter_key; $this->filter_key = $filter_key;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFilterKey() { public function getFilterKey() {
return $this->filterKey; return $this->filter_key;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setUrlProcessor($processor_name) { public function setUrlProcessor($processor_name) {
$this->urlProcessor = $processor_name; $this->url_processor = $processor_name;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getUrlProcessorName() { public function getUrlProcessorName() {
return $this->urlProcessor; return $this->url_processor;
} }
} }
...@@ -33,7 +33,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -33,7 +33,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getWidget(); public function getWidget();
/** /**
* Get field identifier. * Returns field identifier.
* *
* @return string * @return string
* The field identifier of this facet. * The field identifier of this facet.
...@@ -41,7 +41,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -41,7 +41,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getFieldIdentifier(); public function getFieldIdentifier();
/** /**
* Set field identifier. * Sets field identifier.
* *
* @param string $field_identifier * @param string $field_identifier
* The field identifier of this facet. * The field identifier of this facet.
...@@ -52,7 +52,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -52,7 +52,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function setFieldIdentifier($field_identifier); public function setFieldIdentifier($field_identifier);
/** /**
* Get the field alias used to identify the facet in the url. * Returns the field alias used to identify the facet in the url.
* *
* @return string * @return string
* The field alias for the facet. * The field alias for the facet.
...@@ -60,7 +60,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -60,7 +60,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getFieldAlias(); public function getFieldAlias();
/** /**
* Get the field name of the facet as used in the index. * Returns the field name of the facet as used in the index.
* *
* @TODO: Check if fieldIdentifier can be used as well! * @TODO: Check if fieldIdentifier can be used as well!
* *
...@@ -70,9 +70,10 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -70,9 +70,10 @@ interface FacetInterface extends ConfigEntityInterface {
public function getName(); public function getName();
/** /**
* Gets the name of the facet for use in the URL. * Returns the name of the facet for use in the URL.
* *
* @return string * @return string
* The name of the facet for use in the URL.
*/ */
public function getUrlAlias(); public function getUrlAlias();
...@@ -80,6 +81,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -80,6 +81,7 @@ interface FacetInterface extends ConfigEntityInterface {
* Sets the name of the facet for use in the URL. * Sets the name of the facet for use in the URL.
* *
* @param string $url_alias * @param string $url_alias
* The name of the facet for use in the URL.
*/ */
public function setUrlAlias($url_alias); public function setUrlAlias($url_alias);
...@@ -92,7 +94,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -92,7 +94,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function setActiveItem($value); public function setActiveItem($value);
/** /**
* Get all the active items in the facet. * Returns all the active items in the facet.
* *
* @return mixed * @return mixed
* An array containing all active items. * An array containing all active items.
...@@ -111,7 +113,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -111,7 +113,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function isActiveValue($value); public function isActiveValue($value);
/** /**
* Get the result for the facet. * Returns the result for the facet.
* *
* @return \Drupal\facets\Result\ResultInterface[] $results * @return \Drupal\facets\Result\ResultInterface[] $results
* The results of the facet. * The results of the facet.
...@@ -126,69 +128,61 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -126,69 +128,61 @@ interface FacetInterface extends ConfigEntityInterface {
*/ */
public function setResults(array $results); public function setResults(array $results);
/** /**
* Get the query type instance. * Sets an array of unfiltered results.
* *
* @return string * These unfiltered results are used to set the correct count of the actual
* The query type plugin being used. * facet results when using the OR query operator. They are not results value
* objects like those in ::$results.
*
* @param array $all_results
* Unfiltered results.
*/ */
public function getQueryType(); public function setUnfilteredResults(array $all_results = []);
/** /**
* Get the plugin name for the url processor. * Returns an array of unfiltered results.
* *
* @return string * @return array
* The id of the url processor. * Unfiltered results.
*/ */
public function getUrlProcessorName(); public function getUnfilteredResults();
/** /**
* Retrieves an option. * Returns the query type instance.
*
* @param string $name
* The name of an option.
* @param mixed $default
* The value return if the option wasn't set.
* *
* @return mixed * @return string
* The value of the option. * The query type plugin being used.
*
* @see getOptions()
*/ */
public function getOption($name, $default = NULL); public function getQueryType();
/** /**
* Retrieves an array of all options. * Returns the query operator.
* *
* @return array * @return string
* An associative array of option values, keyed by the option name. * The query operator being used.
*/ */
public function getOptions(); public function getQueryOperator();
/** /**
* Sets an option. * Returns the value of the exclude boolean.
* *
* @param string $name * This will return true when the current facet's value should be exclusive
* The name of an option. * from the search rather than inclusive.
* @param mixed $option * When this returns TRUE, the operator will be "<>" instead of "=".
* The new option.
* *
* @return $this * @return bool
* Returns self. * A boolean flag indicating if search should exlude selected facets
*/ */
public function setOption($name, $option); public function getExclude();
/** /**
* Sets the index's options. * Returns the plugin name for the url processor.
* *
* @param array $options * @return string
* The new index options. * The id of the url processor.
*
* @return $this
* Returns self.
*/ */
public function setOptions(array $options); public function getUrlProcessorName();
/** /**
* Sets a string representation of the Facet source plugin. * Sets a string representation of the Facet source plugin.
...@@ -203,6 +197,22 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -203,6 +197,22 @@ interface FacetInterface extends ConfigEntityInterface {
*/ */
public function setFacetSourceId($facet_source_id); public function setFacetSourceId($facet_source_id);
/**
* Sets the query operator.
*
* @param string $operator
* The query operator being used.
*/
public function setQueryOperator($operator);
/**
* Sets the exclude.
*
* @param bool $exclude
* A boolean flag indicating if search should exclude selected facets
*/
public function setExclude($exclude);
/** /**
* Returns the Facet source id. * Returns the Facet source id.
* *
...@@ -228,7 +238,7 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -228,7 +238,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getFacetSourceConfig(); public function getFacetSourceConfig();
/** /**
* Load the facet sources for this facet. * Loads the facet sources for this facet.
* *
* @param bool|TRUE $only_enabled * @param bool|TRUE $only_enabled
* Only return enabled facet sources. * Only return enabled facet sources.
...@@ -266,6 +276,14 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -266,6 +276,14 @@ interface FacetInterface extends ConfigEntityInterface {
*/ */
public function getProcessorsByStage($stage, $only_enabled = TRUE); public function getProcessorsByStage($stage, $only_enabled = TRUE);
/**
* Retrieves this facets's processor configs.
*
* @return array
* An array of processors and their configs.
*/
public function getProcessorConfigs();
/** /**
* Sets the "only visible when facet source is visible" boolean flag. * Sets the "only visible when facet source is visible" boolean flag.
* *
...@@ -287,60 +305,67 @@ interface FacetInterface extends ConfigEntityInterface { ...@@ -287,60 +305,67 @@ interface FacetInterface extends ConfigEntityInterface {
public function getOnlyVisibleWhenFacetSourceIsVisible(); public function getOnlyVisibleWhenFacetSourceIsVisible();
/** /**
* Enabled a processor for this facet. * Adds a processor for this facet.
* *
* @param array $processor * @param array $processor
* An array definition for a processor.
*/ */
public function addProcessor(array $processor); public function addProcessor(array $processor);
/** /**
* Disable a processor for this facet. * Removes a processor for this facet.
* *
* @param string $processor_id * @param string $processor_id
* The plugin id of the processor.
*/ */
public function removeProcessor($processor_id); public function removeProcessor($processor_id);
/** /**
* Define the no-results behavior. * Defines the no-results behavior.
* *
* @param array $behavior * @param array $behavior
* The definition of the behavior.
*/ */
public function setEmptyBehavior($behavior); public function setEmptyBehavior(array $behavior);
/** /**
* Return the defined no-results behavior or NULL if none defined. * Returns the defined no-results behavior or NULL if none defined.
* *
* @return array|NULL * @return array|NULL
* The behavior definition or NULL.
*/ */
public function getEmptyBehavior(); public function getEmptyBehavior();
/** /**
* Return the configuration of the selected widget. * Returns the configuration of the selected widget.
* *
* @return array * @return array
* The configuration settings for the widget.
*/ */
public function getWidgetConfigs(); public function getWidgetConfigs();
/** /**
* Set the configuration for the widget of this facet. * Sets the configuration for the widget of this facet.
* *
* @param array $widget_config * @param array $widget_config
* The configuration settings for the widget.
*/ */
public function setWidgetConfigs(array $widget_config); public function setWidgetConfigs(array $widget_config);
/** /**
* Get any additional configuration for this facet, no defined above. * Returns any additional configuration for this facet, not defined above.
* *
* @return array * @return array
* An array of additional configuration for the facet.
*/ */
public function getFacetConfigs(); public function getFacetConfigs();
/** /**
* Define any additional configuration for this facet not defined above. * Defines any additional configuration for this facet not defined above.
* *
* @param array $facet_config * @param array $facet_config
* An array of additional configuration for the facet.
*/ */
public function setFacetConfigs(array $facet_config); public function setFacetConfigs(array $facet_config);
} }
...@@ -120,7 +120,7 @@ class FacetListBuilder extends ConfigEntityListBuilder { ...@@ -120,7 +120,7 @@ class FacetListBuilder extends ConfigEntityListBuilder {
} }
/** /**
* {@inheritdoc} * Builds an array of facet sources for display in the overview.
*/ */
public function buildFacetSourceRow(array $facet_source = []) { public function buildFacetSourceRow(array $facet_source = []) {
return array( return array(
......