diff --git a/js/search_api_autocomplete.js b/js/search_api_autocomplete.js index 609a566..d2ef399 100644 --- a/js/search_api_autocomplete.js +++ b/js/search_api_autocomplete.js @@ -1,194 +1,199 @@ +/** + * @file + * Expands the behaviour of the default autocompletion. + */ + (function ($) { -// Auto-submit main search input after autocomplete -if (typeof Drupal.jsAC != 'undefined') { - - var getSetting = function (input, setting, defaultValue) { - // Earlier versions of jQuery, like the default for Drupal 7, don't properly - // convert data-* attributes to camel case, so we access it via the verbatim - // name from the attribute (which also works in newer versions). - var search = $(input).data('search-api-autocomplete-search'); - if (typeof search == 'undefined' - || typeof Drupal.settings.search_api_autocomplete == 'undefined' - || typeof Drupal.settings.search_api_autocomplete[search] == 'undefined' - || typeof Drupal.settings.search_api_autocomplete[search][setting] == 'undefined') { - return defaultValue; - } - return Drupal.settings.search_api_autocomplete[search][setting]; - }; - - var oldJsAC = Drupal.jsAC; - /** - * An AutoComplete object. - * - * Overridden to set the proper "role" attribute on the input element. - */ - Drupal.jsAC = function ($input, db) { - if ($input.data('search-api-autocomplete-search')) { - $input.attr('role', 'combobox'); - $input.parent().attr('role', 'search'); - } - oldJsAC.call(this, $input, db); - }; - Drupal.jsAC.prototype = oldJsAC.prototype; - - /** - * Handler for the "keyup" event. - * - * Extend from Drupal's autocomplete.js to automatically submit the form - * when Enter is hit. - */ - var default_onkeyup = Drupal.jsAC.prototype.onkeyup; - Drupal.jsAC.prototype.onkeyup = function (input, e) { - if (!e) { - e = window.event; - } - // Fire standard function. - default_onkeyup.call(this, input, e); - - if (13 == e.keyCode && $(input).hasClass('auto_submit')) { - var selector = getSetting(input, 'selector', ':submit'); - $(selector, input.form).trigger('click'); - } - }; - - /** - * Handler for the "keydown" event. - * - * Extend from Drupal's autocomplete.js to avoid ajax interfering with the - * autocomplete. - */ - var default_onkeydown = Drupal.jsAC.prototype.onkeydown; - Drupal.jsAC.prototype.onkeydown = function (input, e) { - if (!e) { - e = window.event; - } - // Fire standard function. - default_onkeydown.call(this, input, e); - - // Prevent that the ajax handling of Views fires too early and thus - // misses the form update. - if (13 == e.keyCode && $(input).hasClass('auto_submit')) { - e.preventDefault(); - return false; - } - }; - - Drupal.jsAC.prototype.select = function(node) { - var autocompleteValue = $(node).data('autocompleteValue'); - // Check whether this is not a suggestion but a "link". - if (autocompleteValue.charAt(0) == ' ') { - window.location.href = autocompleteValue.substr(1); - return false; - } - this.input.value = autocompleteValue; - $(this.input).trigger('autocompleteSelect', [node]); - if ($(this.input).hasClass('auto_submit')) { - if (typeof Drupal.search_api_ajax != 'undefined') { - // Use Search API Ajax to submit - Drupal.search_api_ajax.navigateQuery($(this.input).val()); + // Auto-submit main search input after autocomplete. + if (typeof Drupal.jsAC != 'undefined') { + + var getSetting = function (input, setting, defaultValue) { + // Earlier versions of jQuery, like the default for Drupal 7, don't properly + // convert data-* attributes to camel case, so we access it via the verbatim + // name from the attribute (which also works in newer versions). + var search = $(input).data('search-api-autocomplete-search'); + if (typeof search == 'undefined' + || typeof Drupal.settings.search_api_autocomplete == 'undefined' + || typeof Drupal.settings.search_api_autocomplete[search] == 'undefined' + || typeof Drupal.settings.search_api_autocomplete[search][setting] == 'undefined') { + return defaultValue; } - else { - var selector = getSetting(this.input, 'selector', ':submit'); - $(selector, this.input.form).trigger('click'); + return Drupal.settings.search_api_autocomplete[search][setting]; + }; + + var oldJsAC = Drupal.jsAC; + /** + * An AutoComplete object. + * + * Overridden to set the proper "role" attribute on the input element. + */ + Drupal.jsAC = function ($input, db) { + if ($input.data('search-api-autocomplete-search')) { + $input.attr('role', 'combobox'); + $input.parent().attr('role', 'search'); + } + oldJsAC.call(this, $input, db); + }; + Drupal.jsAC.prototype = oldJsAC.prototype; + + /** + * Handler for the "keyup" event. + * + * Extend from Drupal's autocomplete.js to automatically submit the form + * when Enter is hit. + */ + var default_onkeyup = Drupal.jsAC.prototype.onkeyup; + Drupal.jsAC.prototype.onkeyup = function (input, e) { + if (!e) { + e = window.event; + } + // Fire standard function. + default_onkeyup.call(this, input, e); + + if (13 == e.keyCode && $(input).hasClass('auto_submit')) { + var selector = getSetting(input, 'selector', ':submit'); + $(selector, input.form).trigger('click'); + } + }; + + /** + * Handler for the "keydown" event. + * + * Extend from Drupal's autocomplete.js to avoid ajax interfering with the + * autocomplete. + */ + var default_onkeydown = Drupal.jsAC.prototype.onkeydown; + Drupal.jsAC.prototype.onkeydown = function (input, e) { + if (!e) { + e = window.event; } + // Fire standard function. + default_onkeydown.call(this, input, e); + + // Prevent that the ajax handling of Views fires too early and thus + // misses the form update. + if (13 == e.keyCode && $(input).hasClass('auto_submit')) { + e.preventDefault(); + return false; + } + }; + + Drupal.jsAC.prototype.select = function(node) { + var autocompleteValue = $(node).data('autocompleteValue'); + // Check whether this is not a suggestion but a "link". + if (autocompleteValue.charAt(0) == ' ') { + window.location.href = autocompleteValue.substr(1); + return false; + } + this.input.value = autocompleteValue; + $(this.input).trigger('autocompleteSelect', [node]); + if ($(this.input).hasClass('auto_submit')) { + if (typeof Drupal.search_api_ajax != 'undefined') { + // Use Search API Ajax to submit. + Drupal.search_api_ajax.navigateQuery($(this.input).val()); + } + else { + var selector = getSetting(this.input, 'selector', ':submit'); + $(selector, this.input.form).trigger('click'); + } + return true; + } + }; + + /** + * Overwrite default behaviour. + * + * Just always return true to make it possible to submit even when there was + * an autocomplete suggestion list open. + */ + Drupal.autocompleteSubmit = function () { + $('#autocomplete').each(function () { + this.owner.hidePopup(); + }); return true; - } - }; - - /** - * Overwrite default behaviour. - * - * Just always return true to make it possible to submit even when there was - * an autocomplete suggestion list open. - */ - Drupal.autocompleteSubmit = function () { - $('#autocomplete').each(function () { - this.owner.hidePopup(); - }); - return true; - }; - - /** - * Performs a cached and delayed search. - */ - Drupal.ACDB.prototype.search = function (searchString) { - this.searchString = searchString; - - // Check allowed length of string for autocomplete. - var data = $(this.owner.input).first().data('min-autocomplete-length'); - if (data && searchString.length < data) { - return; - } - - // See if this string needs to be searched for anyway. - if (searchString.match(/^\s*$/)) { - return; - } - - // Prepare search string. - searchString = searchString.replace(/^\s+/, ''); - searchString = searchString.replace(/\s+/g, ' '); - - // See if this key has been searched for before. - if (this.cache[searchString]) { - return this.owner.found(this.cache[searchString]); - } - - var db = this; - this.searchString = searchString; - - // Initiate delayed search. - if (this.timer) { - clearTimeout(this.timer); - } - var sendAjaxRequest = function () { - db.owner.setStatus('begin'); - - var url; - - // Allow custom Search API Autocomplete overrides for specific searches. - if (getSetting(db.owner.input, 'custom_path', false)) { - var queryChar = db.uri.indexOf('?') >= 0 ? '&' : '?'; - url = db.uri + queryChar + 'search=' + encodeURIComponent(searchString); + }; + + /** + * Performs a cached and delayed search. + */ + Drupal.ACDB.prototype.search = function (searchString) { + this.searchString = searchString; + + // Check allowed length of string for autocomplete. + var data = $(this.owner.input).first().data('min-autocomplete-length'); + if (data && searchString.length < data) { + return; } - else { - // We use Drupal.encodePath instead of encodeURIComponent to allow - // autocomplete search terms to contain slashes. - url = db.uri + '/' + Drupal.encodePath(searchString); + + // See if this string needs to be searched for anyway. + if (searchString.match(/^\s*$/)) { + return; } - // Ajax GET request for autocompletion. - $.ajax({ - type: 'GET', - url: url, - dataType: 'json', - success: function (matches) { - if (typeof matches.status == 'undefined' || matches.status != 0) { - db.cache[searchString] = matches; - // Verify if these are still the matches the user wants to see. - if (db.searchString == searchString) { - db.owner.found(matches); + // Prepare search string. + searchString = searchString.replace(/^\s+/, ''); + searchString = searchString.replace(/\s+/g, ' '); + + // See if this key has been searched for before. + if (this.cache[searchString]) { + return this.owner.found(this.cache[searchString]); + } + + var db = this; + this.searchString = searchString; + + // Initiate delayed search. + if (this.timer) { + clearTimeout(this.timer); + } + var sendAjaxRequest = function () { + db.owner.setStatus('begin'); + + var url; + + // Allow custom Search API Autocomplete overrides for specific searches. + if (getSetting(db.owner.input, 'custom_path', false)) { + var queryChar = db.uri.indexOf('?') >= 0 ? '&' : '?'; + url = db.uri + queryChar + 'search=' + encodeURIComponent(searchString); + } + else { + // We use Drupal.encodePath instead of encodeURIComponent to allow + // autocomplete search terms to contain slashes. + url = db.uri + '/' + Drupal.encodePath(searchString); + } + + // Ajax GET request for autocompletion. + $.ajax({ + type: 'GET', + url: url, + dataType: 'json', + success: function (matches) { + if (typeof matches.status == 'undefined' || matches.status != 0) { + db.cache[searchString] = matches; + // Verify if these are still the matches the user wants to see. + if (db.searchString == searchString) { + db.owner.found(matches); + } + db.owner.setStatus('found'); + } + }, + error: function (xmlhttp) { + if (xmlhttp.status) { + alert(Drupal.ajaxError(xmlhttp, db.uri)); } - db.owner.setStatus('found'); - } - }, - error: function (xmlhttp) { - if (xmlhttp.status) { - alert(Drupal.ajaxError(xmlhttp, db.uri)); } - } - }); + }); + }; + // Make it possible to override the delay via a setting. + var delay = getSetting(this.owner.input, 'delay', this.delay); + if (delay > 0) { + this.timer = setTimeout(sendAjaxRequest, delay); + } + else { + sendAjaxRequest.apply(); + } }; - // Make it possible to override the delay via a setting. - var delay = getSetting(this.owner.input, 'delay', this.delay); - if (delay > 0) { - this.timer = setTimeout(sendAjaxRequest, delay); - } - else { - sendAjaxRequest.apply(); - } - }; -} + } })(jQuery); diff --git a/search_api_autocomplete.api.php b/search_api_autocomplete.api.php index 409fd24..63e477b 100644 --- a/search_api_autocomplete.api.php +++ b/search_api_autocomplete.api.php @@ -130,12 +130,12 @@ function hook_search_api_autocomplete_search_load(array $searches) { * * This hook is invoked after the search is inserted into the database. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search that is being inserted. * * @see hook_entity_insert() */ -function hook_search_api_autocomplete_search_insert(SearchApiAutocompleteSearch $search) { +function hook_search_api_autocomplete_search_insert(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search) { db_insert('mytable') ->fields(array( 'id' => entity_id('search_api_autocomplete_search', $search), @@ -149,12 +149,12 @@ function hook_search_api_autocomplete_search_insert(SearchApiAutocompleteSearch * * This hook is invoked before the search is saved to the database. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search that is being inserted or updated. * * @see hook_entity_presave() */ -function hook_search_api_autocomplete_search_presave(SearchApiAutocompleteSearch $search) { +function hook_search_api_autocomplete_search_presave(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search) { $search->name = 'foo'; } @@ -163,12 +163,12 @@ function hook_search_api_autocomplete_search_presave(SearchApiAutocompleteSearch * * This hook is invoked after the search has been updated in the database. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search that is being updated. * * @see hook_entity_update() */ -function hook_search_api_autocomplete_search_update(SearchApiAutocompleteSearch $search) { +function hook_search_api_autocomplete_search_update(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search) { db_update('mytable') ->fields(array('extra' => print_r($search, TRUE))) ->condition('id', entity_id('search_api_autocomplete_search', $search)) @@ -180,12 +180,12 @@ function hook_search_api_autocomplete_search_update(SearchApiAutocompleteSearch * * This hook is invoked after the search has been removed from the database. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search that is being deleted. * * @see hook_entity_delete() */ -function hook_search_api_autocomplete_search_delete(SearchApiAutocompleteSearch $search) { +function hook_search_api_autocomplete_search_delete(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search) { db_delete('mytable') ->condition('pid', entity_id('search_api_autocomplete_search', $search)) ->execute(); @@ -201,7 +201,7 @@ function hook_search_api_autocomplete_search_delete(SearchApiAutocompleteSearch */ function hook_default_search_api_autocomplete_search() { $defaults['main'] = entity_create('search_api_autocomplete_search', array( - // … + // …. )); return $defaults; } @@ -221,7 +221,7 @@ function hook_default_search_api_autocomplete_search_alter(array &$defaults) { /** * Alter autocomplete suggestions. * - * @param array $suggestions + * @param \Drupal\search_api_autocomplete\SuggestionInterface[] $suggestions * Associative array where keys are the complete suggested keywords, and the * values are suggestion arrays as defined by * SearchApiAutocompleteInterface::getAutocompleteSuggestions(). @@ -240,12 +240,12 @@ function hook_search_api_autocomplete_suggestions_alter(array &$suggestions, arr * * @param string[] $fields * The machine names of the detected fulltext fields. - * @param \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The related search autocomplete object. * @param view $view * The view for which fulltext fields should be determined. */ -function hook_search_api_autocomplete_views_fulltext_fields_alter(array &$fields, \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search, view $view) { +function hook_search_api_autocomplete_views_fulltext_fields_alter(array &$fields, \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search, view $view) { // Add a new field. $fields[] = 'extra_search_api_views_fulltext'; } @@ -295,7 +295,7 @@ function example_list_autocomplete_searches(\Drupal\search_api\IndexInterface $i /** * Create the query that would be issued for the given search for the complete keys. * - * @param \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search for which to create the query. * @param $complete * A string containing the complete search keys. @@ -309,7 +309,7 @@ function example_list_autocomplete_searches(\Drupal\search_api\IndexInterface $i * @throws \Drupal\search_api\SearchApiException * If the query couldn't be created. */ -function example_create_autocomplete_query(\Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $complete, $incomplete) { +function example_create_autocomplete_query(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $complete, $incomplete) { $query = search_api_query($search->index_id); if ($complete) { $query->keys($complete); @@ -334,13 +334,13 @@ function example_create_autocomplete_query(\Drupal\search_api_autocomplete\Entit * rely on knowing the array structure (like the elements' parents) and should * not set "#tree" to FALSE for any element. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search whose config form should be presented. * * @see example_autocomplete_config_form_validate() * @see example_autocomplete_config_form_submit() */ -function example_autocomplete_config_form(array $form, array &$form_state, \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search) { +function example_autocomplete_config_form(array $form, array &$form_state, \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface) { $form['user_filters'] = array( '#type' => 'textarea', '#title' => t('Custom filters'), @@ -452,7 +452,7 @@ function example_search_api_query_alter(SearchApiQueryInterface $query) { /** * Returns the URL to use for a custom script. * - * @param SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterfaceh $search * The autocomplete search in question. * @param array $element * The form element for which autocompletion is being added. @@ -465,7 +465,7 @@ function example_search_api_query_alter(SearchApiQueryInterface $query) { * * @ingroup callbacks */ -function callback_search_api_autocomplete_script_url(SearchApiAutocompleteSearch $search, array $element, array $config) { +function callback_search_api_autocomplete_script_url(\Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search, array $element, array $config) { // Solution to use a custom script on multilingual sites which have the // current language as the first path element. global $language; diff --git a/search_api_autocomplete.libraries.yml b/search_api_autocomplete.libraries.yml index 7883d15..dcc3093 100644 --- a/search_api_autocomplete.libraries.yml +++ b/search_api_autocomplete.libraries.yml @@ -4,3 +4,5 @@ search_api_autocomplete: css: theme: css/search_api_autocomplete.css: {} + dependencies: + - core/drupal.autocomplete diff --git a/search_api_autocomplete.module b/search_api_autocomplete.module index 0370872..2d4b248 100644 --- a/search_api_autocomplete.module +++ b/search_api_autocomplete.module @@ -5,6 +5,7 @@ * Adds autocomplete capabilities for Search API searches. */ +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Url; // Include the files with the module-specific implementations. @@ -32,7 +33,7 @@ function search_api_autocomplete_theme() { /** * Implements hook_entity_operation(). */ -function search_api_autocomplete_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) { +function search_api_autocomplete_entity_operation(EntityInterface $entity) { if ($entity->getEntityTypeId() != 'search_api_index') { return []; } diff --git a/search_api_autocomplete.routing.yml b/search_api_autocomplete.routing.yml index 7656e00..b8c857e 100644 --- a/search_api_autocomplete.routing.yml +++ b/search_api_autocomplete.routing.yml @@ -1,5 +1,5 @@ search_api_autocomplete.autocomplete: - path: /search_api_autocomplete/{search_api_autocomplete_settings}/{fields} + path: /search_api_autocomplete/{search_api_autocomplete_search}/{fields} defaults: _controller: \Drupal\search_api_autocomplete\Controller\AutocompleteController::autocomplete fields: '' diff --git a/search_api_autocomplete.services.yml b/search_api_autocomplete.services.yml index 9af9fc9..cee5413 100644 --- a/search_api_autocomplete.services.yml +++ b/search_api_autocomplete.services.yml @@ -1,7 +1,7 @@ services: plugin_manager.search_api_autocomplete_type: - class: \Drupal\search_api_autocomplete\Plugin\AutocompleteTypeManager + class: \Drupal\search_api_autocomplete\Type\TypeManager parent: default_plugin_manager plugin_manager.search_api_autocomplete_suggester: - class: \Drupal\search_api_autocomplete\Plugin\AutocompleteSuggesterManager + class: \Drupal\search_api_autocomplete\Suggester\SuggesterManager parent: default_plugin_manager diff --git a/src/Annotation/AutocompleteSuggester.php b/src/Annotation/AutocompleteSuggester.php deleted file mode 100644 index dbcc219..0000000 --- a/src/Annotation/AutocompleteSuggester.php +++ /dev/null @@ -1,16 +0,0 @@ -renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('renderer') + ); + } /** * Page callback for getting autocomplete suggestions. * - * @param \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search_api_autocomplete_settings + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search_api_autocomplete_search * The search for which to retrieve autocomplete suggestions. * @param string $fields * A comma-separated list of fields on which to do autocompletion. Or "-" @@ -26,73 +57,45 @@ class AutocompleteController { * * @return \Drupal\Core\Cache\CacheableJsonResponse * The autocompletion response. - * */ - public function autocomplete(SearchApiAutocompleteSearch $search_api_autocomplete_settings, $fields, Request $request) { + public function autocomplete(SearchApiAutocompleteSearchInterface $search_api_autocomplete_search, $fields, Request $request) { $matches = []; try { - if ($search_api_autocomplete_settings->supportsAutocompletion()) { + if ($search_api_autocomplete_search->supportsAutocompletion()) { $keys = $request->query->get('q'); - list($complete, $incomplete) = $search_api_autocomplete_settings->splitKeys($keys); - $query = $search_api_autocomplete_settings->getQuery($complete, $incomplete); + list($complete, $incomplete) = $search_api_autocomplete_search->splitKeys($keys); + $query = $search_api_autocomplete_search->getQuery($complete, $incomplete); if ($query) { // @todo Maybe make range configurable? $query->range(0, 10); - $query->setOption('search id', 'search_api_autocomplete:' . $search_api_autocomplete_settings->id()); - if (!empty($search_api_autocomplete_settings->getOption('fields'))) { - $query->setFulltextFields($search_api_autocomplete_settings->getOption('fields')); + $query->setOption('search id', 'search_api_autocomplete:' . $search_api_autocomplete_search->id()); + if (!empty($search_api_autocomplete_search->getOption('fields'))) { + $query->setFulltextFields($search_api_autocomplete_search->getOption('fields')); } elseif ($fields != '-') { $fields = explode(' ', $fields); $query->setFulltextFields($fields); } $query->preExecute(); - $suggestions = $search_api_autocomplete_settings->getSuggester()->getAutocompleteSuggestions($query, $incomplete, $keys); + $suggestions = $search_api_autocomplete_search->getSuggester()->getAutocompleteSuggestions($query, $incomplete, $keys); if ($suggestions) { foreach ($suggestions as $suggestion) { - // Convert suggestion strings into an array. - if (is_string($suggestion)) { - $pos = strpos($suggestion, $keys); - if ($pos === FALSE) { - $suggestion = [ - 'user_input' => '', - 'suggestion_suffix' => $suggestion, - ]; - } - else { - $suggestion = [ - 'suggestion_prefix' => substr($suggestion, 0, $pos), - 'user_input' => $keys, - 'suggestion_suffix' => substr($suggestion, $pos + strlen($keys)), - ]; - } - } - // Add defaults. - $suggestion += [ - 'url' => NULL, - 'keys' => NULL, - 'prefix' => NULL, - 'suggestion_prefix' => '', - 'user_input' => $keys, - 'suggestion_suffix' => '', - 'results' => NULL, - ]; - if (empty($search_api_autocomplete_settings->getOption('results'))) { - unset($suggestion['results']); + if (empty($search_api_autocomplete_search->getOption('results'))) { + $suggestion->setResults(NULL); } // Decide what the action of the suggestion is – entering specific // search terms or redirecting to a URL. - if (isset($suggestion['url'])) { - $key = ' ' . $suggestion['url']; + if ($suggestion->getUrl()) { + $key = ' ' . $suggestion->getUrl(); } else { - // Also set the "keys" key so it will always be available in alter - // hooks and the theme function. - if (!isset($suggestion['keys'])) { - $suggestion['keys'] = $suggestion['suggestion_prefix'] . $suggestion['user_input'] . $suggestion['suggestion_suffix']; + // Also set the "keys" key so it will always be available in + // alter hooks and the theme function. + if (!$suggestion->getKeys()) { + $suggestion->setKeys($suggestion->getSuggestionPrefix() . $suggestion->getUserInput() . $suggestion->getSuggestionSuffix()); } - $key = trim($suggestion['keys']); + $key = trim($suggestion->getKeys()); } if (!isset($ret[$key])) { @@ -102,31 +105,31 @@ class AutocompleteController { $alter_params = [ 'query' => $query, - 'search' => $search_api_autocomplete_settings, + 'search' => $search_api_autocomplete_search, 'incomplete_key' => $incomplete, 'user_input' => $keys, ]; - \Drupal::moduleHandler()->alter('search_api_autocomplete_suggestions', $ret, $alter_params); + $this->moduleHandler()->alter('search_api_autocomplete_suggestions', $ret, $alter_params); + /*** @var \Drupal\search_api_autocomplete\SuggestionInterface $suggestion */ foreach ($ret as $key => $suggestion) { - if (isset($suggestion['render'])) { + if ($build = $suggestion->getRender()) { $matches[] = [ 'value' => $key, - 'label' => \Drupal::service('renderer')->render($suggestion['render']), + 'label' => $this->renderer->render($build), ]; } else { $ret[$key] = [ '#theme' => 'search_api_autocomplete_suggestion', - ] - // Convert the suggestion into a suitable variable for + ] + array_combine(array_map(function ($key) { + // Convert the suggestion into a suitable variable for // templates, by adding # in front. - + array_combine(array_map(function ($key) { - return '#' . $key; - }, array_keys($suggestion)), array_values($suggestion)); + return '#' . $key; + }, array_keys($suggestion)), array_values($suggestion)); $matches[] = [ 'value' => $key, - 'label' => \Drupal::service('renderer')->render($ret[$key]), + 'label' => $this->renderer->render($ret[$key]), ]; } } @@ -144,7 +147,7 @@ class AutocompleteController { /** * Checks access to the autocompletion route. * - * @param \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search_api_autocomplete_settings + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search_api_autocomplete_search * The configured autocompletion search. * @param \Drupal\Core\Session\AccountInterface $account * The account. @@ -152,10 +155,10 @@ class AutocompleteController { * @return \Drupal\Core\Access\AccessResult * The access result. */ - public function access(SearchApiAutocompleteSearch $search_api_autocomplete_settings, AccountInterface $account) { - $access = AccessResult::allowedIf($search_api_autocomplete_settings->status()) - ->andIf(AccessResult::allowedIfHasPermission($account, 'use search_api_autocomplete for ' . $search_api_autocomplete_settings->id())) - ->andIf(AccessResult::allowedIf($search_api_autocomplete_settings->supportsAutocompletion())); + public function access(SearchApiAutocompleteSearchInterface $search_api_autocomplete_search, AccountInterface $account) { + $access = AccessResult::allowedIf($search_api_autocomplete_search->status()) + ->andIf(AccessResult::allowedIfHasPermission($account, 'use search_api_autocomplete for ' . $search_api_autocomplete_search->id())) + ->andIf(AccessResult::allowedIf($search_api_autocomplete_search->supportsAutocompletion())); return $access; } diff --git a/src/Entity/SearchApiAutocompleteSearch.php b/src/Entity/SearchApiAutocompleteSearch.php index 244f8e2..c9d95d6 100644 --- a/src/Entity/SearchApiAutocompleteSearch.php +++ b/src/Entity/SearchApiAutocompleteSearch.php @@ -6,12 +6,13 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\search_api\Entity\Index; use Drupal\search_api_autocomplete\Controller\AutocompleteController; +use Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface; /** * Describes the autocomplete settings for a certain search. * * @ConfigEntityType( - * id = "search_api_autocomplete_settings", + * id = "search_api_autocomplete_search", * label = @Translation("Autocomplete search"), * handlers = { * "form" = { @@ -31,8 +32,8 @@ use Drupal\search_api_autocomplete\Controller\AutocompleteController; * "label" = "label" * }, * links = { - * "edit-form" = "/admin/config/search/search-api/index/autocomplete/{search_api_autocomplete_settings}/edit", - * "delete-form" = "/admin/config/search/search-api/index/autocomplete/{search_api_autocomplete_settings}/delete", + * "edit-form" = "/admin/config/search/search-api/index/autocomplete/{search_api_autocomplete_search}/edit", + * "delete-form" = "/admin/config/search/search-api/index/autocomplete/{search_api_autocomplete_search}/delete", * }, * config_export = { * "id", @@ -45,31 +46,39 @@ use Drupal\search_api_autocomplete\Controller\AutocompleteController; * } * ) */ -class SearchApiAutocompleteSearch extends ConfigEntityBase { - - // Entity properties, loaded from the database: +class SearchApiAutocompleteSearch extends ConfigEntityBase implements SearchApiAutocompleteSearchInterface { /** + * The entity ID. + * * @var string */ protected $id; /** + * The entity label. + * * @var string */ protected $label; /** - * @var integer + * The index ID. + * + * @var string */ protected $index_id; /** + * The suggester ID. + * * @var string */ protected $suggester_id; /** + * The autocomplete type. + * * @var string */ protected $type; @@ -83,16 +92,18 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { * * @var array */ - protected $options = array(); - - // Inferred properties, for caching: + protected $options = []; /** + * The searchapi index instance. + * * @var \Drupal\search_api\IndexInterface */ protected $index; /** + * The searchapi server instance. + * * @var \Drupal\search_api\ServerInterface */ protected $server; @@ -100,7 +111,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { /** * The suggester plugin this search uses. * - * @var \Drupal\search_api_autocomplete\AutocompleteSuggesterInterface + * @var \Drupal\search_api_autocomplete\Suggester\SuggesterInterface */ protected $suggester; @@ -143,15 +154,15 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * Retrieves the ID of the suggester plugin for this search. - * - * @return string - * This search's suggester plugin's ID. + * {@inheritdoc} */ public function getSuggesterId() { return $this->suggester_id; } + /** + * {@inheritdoc} + */ public function setSuggesterId($suggester_id) { $this->suggester_id = $suggester_id; unset($this->suggester); @@ -159,14 +170,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * Retrieves the suggester plugin for this search. - * - * @param bool $reset - * (optional) If TRUE, clear the internal static cache and reload the - * suggester. - * - * @return \Drupal\search_api_autocomplete\AutocompleteSuggesterInterface|null - * This search's suggester plugin, or NULL if it could not be loaded. + * {@inheritdoc} */ public function getSuggester($reset = FALSE) { if (!isset($this->suggester) || $reset) { @@ -178,7 +182,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { $variables['@search'] = $this->id(); $variables['@index'] = $this->index() ? $this->index()->label() : $this->index_id; $variables['@suggester_id'] = $this->suggester_id; - \Drupal::logger('search_api_autocomplete')->error('Autocomplete search @search on index @index specifies an invalid suggester plugin @suggester_id.', $variables); + $this->getLogger()->error('Autocomplete search @search on index @index specifies an invalid suggester plugin @suggester_id.', $variables); $this->suggester = FALSE; } } @@ -186,6 +190,8 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** + * Returns the autocomplete suggester plugin manager. + * * @return \Drupal\Component\Plugin\PluginManagerInterface */ protected function getSuggesterManager() { @@ -193,6 +199,15 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** + * Returns a logger. + * + * @return \Psr\Log\LoggerInterface + */ + protected function getLogger() { + return \Drupal::logger('search_api_autocomplete'); + } + + /** * Determines whether autocompletion is currently supported for this search. * * @return bool @@ -211,7 +226,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { */ public function alterElement(array &$element, array $fields = []) { // @todo find a cleaner solution. - $controller = new AutocompleteController(); + $controller = new AutocompleteController(\Drupal::service('renderer')); $config = \Drupal::configFactory()->get('search_api_autocomplete.settings'); if ($controller->access($this, \Drupal::currentUser())->isAllowed()) { // Add option defaults (in case of updates from earlier versions). @@ -224,7 +239,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { $fields_string = $fields ? implode(' ', $fields) : '-'; $autocomplete_route_name = 'search_api_autocomplete.autocomplete'; - $autocomplete_route_parameters = ['search_api_autocomplete_settings' => $this->id(), 'fields' => $fields_string]; + $autocomplete_route_parameters = ['search_api_autocomplete_search' => $this->id(), 'fields' => $fields_string]; $js_settings = []; if ($options['submit_button_selector'] != ':submit') { @@ -236,22 +251,21 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { // Allow overriding of the default handler with a custom script. // @todo implement that. -// $path_overrides = $config->get('scripts') ?: []; -// if (!empty($path_overrides[$this->id()])) { -// $autocomplete_path = NULL; -// $override = $path_overrides[$this->id()]; -// if (is_scalar($override)) { -// $autocomplete_path = url($override, ['absolute' => TRUE, 'query' => ['machine_name' => $this->id()]]); -// } -// elseif (!empty($override['#callback']) && is_callable($override['#callback'])) { -// $autocomplete_path = call_user_func($override['#callback'], $this, $element, $override); -// } -// if (!$autocomplete_path) { -// return; -// } -// $js_settings['custom_path'] = TRUE; -// } - + // $path_overrides = $config->get('scripts') ?: []; + // if (!empty($path_overrides[$this->id()])) { + // $autocomplete_path = NULL; + // $override = $path_overrides[$this->id()]; + // if (is_scalar($override)) { + // $autocomplete_path = url($override, ['absolute' => TRUE, 'query' => ['machine_name' => $this->id()]]); + // } + // elseif (!empty($override['#callback']) && is_callable($override['#callback'])) { + // $autocomplete_path = call_user_func($override['#callback'], $this, $element, $override); + // } + // if (!$autocomplete_path) { + // return; + // } + // $js_settings['custom_path'] = TRUE; + // }. $element['#attached']['library'][] = 'search_api_autocomplete/search_api_autocomplete'; if ($js_settings) { $element['#attached']['drupalSettings'][] = [ @@ -264,7 +278,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { $element['#autocomplete_route_name'] = $autocomplete_route_name; $element['#autocomplete_route_parameters'] = $autocomplete_route_parameters; $element += ['#attributes' => []]; - $element['#attributes'] += ['class'=> []]; + $element['#attributes'] += ['class' => []]; if ($options['autosubmit']) { $element['#attributes']['class'][] = 'auto_submit'; } @@ -323,16 +337,16 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @return \Drupal\search_api_autocomplete\AutocompleteTypeInterface + * Returns the autocomplete instance for this autocomplete search. + * + * @return \Drupal\search_api_autocomplete\Type\TypeInterface */ protected function getTypeInstance() { return \Drupal::service('plugin_manager.search_api_autocomplete_type')->createInstance($this->getType()); } /** - * @param string $label - * - * @return $this + * {@inheritdoc} */ public function setLabel($label) { $this->label = $label; @@ -340,12 +354,15 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @return int + * {@inheritdoc} */ public function getIndexId() { return $this->index_id; } + /** + * {@inheritdoc} + */ public function getIndexInstance() { if (!isset($this->index)) { $this->index = Index::load($this->getIndexId()); @@ -354,9 +371,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @param int $index_id - * - * @return $this + * {@inheritdoc} */ public function setIndexId($index_id) { $this->index_id = $index_id; @@ -364,16 +379,14 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @return string + * {@inheritdoc} */ public function getType() { return $this->type; } /** - * @param string $type - * - * @return $this + * {@inheritdoc} */ public function setType($type) { $this->type = $type; @@ -381,16 +394,14 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @return array + * {@inheritdoc} */ public function getOptions() { return $this->options; } /** - * @param array $options - * - * @return $this + * {@inheritdoc} */ public function setOptions($options) { $this->options = $options; @@ -398,9 +409,7 @@ class SearchApiAutocompleteSearch extends ConfigEntityBase { } /** - * @param $key - * - * @return mixed|null + * {@inheritdoc} */ public function getOption($key, $default = NULL) { $parts = explode('.', $key); diff --git a/src/Form/AutocompleteSearchAdminOverview.php b/src/Form/AutocompleteSearchAdminOverview.php index cd9d2d4..0aa6771 100644 --- a/src/Form/AutocompleteSearchAdminOverview.php +++ b/src/Form/AutocompleteSearchAdminOverview.php @@ -2,21 +2,88 @@ namespace Drupal\search_api_autocomplete\Form; +use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Routing\RedirectDestinationInterface; use Drupal\Core\Url; use Drupal\search_api\IndexInterface; use Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch; +use Drupal\search_api_autocomplete\Type\TypeManager; +use Symfony\Component\DependencyInjection\ContainerInterface; +/** + * Defines the overview of all search autocompletion configurations. + */ class AutocompleteSearchAdminOverview extends FormBase { /** - * @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch + * @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface */ protected $entity; /** + * The autocomplete suggester manager. + * + * @var \Drupal\Component\Plugin\PluginManagerInterface + */ + protected $suggesterManager; + + /** + * The autocomplete type manager. + * + * @var \Drupal\search_api_autocomplete\Type\TypeManager + */ + protected $typeManager; + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * The redirect destination. + * + * @var \Drupal\Core\Routing\RedirectDestinationInterface + */ + protected $destination; + + /** + * Creates a new AutocompleteSearchAdminOverview instance. + * + * @param \Drupal\Component\Plugin\PluginManagerInterface $suggester_manager + * The suggester manager. + * @param \Drupal\search_api_autocomplete\Type\TypeManager $autocomplete_type_manager + * The autocomplete type manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination + * The redirect destination. + */ + public function __construct(PluginManagerInterface $suggester_manager, TypeManager $autocomplete_type_manager, EntityTypeManagerInterface $entity_type_manager, RedirectDestinationInterface $redirect_destination) { + $this->suggesterManager = $suggester_manager; + $this->typeManager = $autocomplete_type_manager; + $this->entityTypeManager = $entity_type_manager; + $this->redirectDestination = $redirect_destination; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin_manager.search_api_autocomplete_suggester'), + $container->get('plugin_manager.search_api_autocomplete_type'), + $container->get('entity_type.manager'), + $container->get('redirect.destination') + ); + } + + /** * {@inheritdoc} */ public function getFormId() { @@ -24,6 +91,23 @@ class AutocompleteSearchAdminOverview extends FormBase { } /** + * Form submission handler for deleting an autocomplete search. + */ + public function submitDelete(array $form, FormStateInterface $form_state) { + /** @var \Drupal\search_api\IndexInterface $index */ + $index = $form_state->get('index'); + $ids = array_keys($this->loadAutocompleteSearchByIndex($index->id())); + if ($ids) { + entity_delete_multiple('search_api_autocomplete_search', $ids); + drupal_set_message($this->t('All autocompletion settings stored for this index were deleted.')); + } + else { + drupal_set_message($this->t('There were no settings to delete.'), 'warning'); + } + $form_state->setRedirectUrl($index->toUrl()); + } + + /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, IndexInterface $search_api_index = NULL) { @@ -54,13 +138,11 @@ class AutocompleteSearchAdminOverview extends FormBase { } $form['#tree'] = TRUE; - /** @var \Drupal\search_api_autocomplete\Plugin\AutocompleteTypeManager $type_manager */ - $type_manager = \Drupal::service('plugin_manager.search_api_autocomplete_type'); - $types = array_map(function ($definition) use ($type_manager) { - return $type_manager->createInstance($definition['id']); - }, $type_manager->getDefinitions()); + $types = array_map(function ($definition) { + return $this->typeManager->createInstance($definition['id']); + }, $this->typeManager->getDefinitions()); $searches = $this->loadAutocompleteSearchByIndex($index_id); - /** @var \Drupal\search_api_autocomplete\AutocompleteTypeInterface $autocomplete_type */ + /** @var \Drupal\search_api_autocomplete\Type\TypeInterface $autocomplete_type */ $searches_by_type = []; $unavailables_by_type = []; foreach ($types as $type => $autocomplete_type) { @@ -90,13 +172,13 @@ class AutocompleteSearchAdminOverview extends FormBase { } } } - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ foreach ($searches as $id => $search) { $type = isset($types[$search->getType()]) ? $search->getType() : ''; $searches_by_type[$type][$id] = $search; $unavailables_by_type[$type][$id] = TRUE; } - /** @var \Drupal\search_api_autocomplete\AutocompleteTypeInterface $autocomplete_type */ + /** @var \Drupal\search_api_autocomplete\Type\TypeInterface $autocomplete_type */ foreach ($types as $type => $autocomplete_type) { if (empty($searches_by_type[$type])) { @@ -126,7 +208,7 @@ class AutocompleteSearchAdminOverview extends FormBase { ]; $form[$type]['searches']['#empty'] = ''; $form[$type]['searches']['#js_select'] = TRUE; - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ foreach ($searches_by_type[$type] as $id => $search) { $form[$type]['searches'][$id] = [ '#type' => 'checkbox', @@ -156,12 +238,13 @@ class AutocompleteSearchAdminOverview extends FormBase { ]; } - if ($items) { - $options['operations'] = ['data' => [ - '#type' => 'operations', - '#links' => $items, - ]]; + $options['operations'] = [ + 'data' => [ + '#type' => 'operations', + '#links' => $items, + ], + ]; } else { $options['operations'] = ''; @@ -183,45 +266,18 @@ class AutocompleteSearchAdminOverview extends FormBase { return $form; } - protected function getSuggestersForIndex(IndexInterface $index) { - /** @var \Drupal\Component\Plugin\PluginManagerInterface $manager */ - $manager = \Drupal::service('plugin_manager.search_api_autocomplete_suggester'); - - $suggesters = array_map(function ($suggester_info) { - return $suggester_info['class']; - }, $manager->getDefinitions()); - $suggesters = array_filter($suggesters, function ($suggester_class) use ($index) { - return $suggester_class::supportsIndex($index); - }); - return $suggesters; - } - - /** - * @param string $index_id - * The index ID. - * - * @return \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch[] - */ - protected function loadAutocompleteSearchByIndex($index_id) { - return \Drupal::entityTypeManager() - ->getStorage('search_api_autocomplete_settings') - ->loadByProperties([ - 'index_id' => $index_id, - ]); - } - /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $messages = $this->t('The settings have been saved.'); foreach ($form_state->getValue('searches') as $id => $enabled) { - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ $search = $form_state->get(['searches', $id]); if ($search->status() != $enabled) { $change = TRUE; if (!empty($search)) { - $options['query'] = \Drupal::destination()->getAsArray(); + $options['query'] = $this->destination->getAsArray(); $options['fragment'] = 'module-search_api_autocomplete'; $vars[':perm_url'] = Url::fromRoute('user.admin_permissions', [], $options)->toString(); $messages = $this->t('The settings have been saved. Please remember to set the permissions for the newly enabled searches.', $vars); @@ -233,21 +289,35 @@ class AutocompleteSearchAdminOverview extends FormBase { drupal_set_message(empty($change) ? $this->t('No values were changed.') : $messages); } - public function submitDelete(array &$form, FormStateInterface $form_state) { - /** @var \Drupal\search_api\IndexInterface $index */ - $index = $form_state->get('index'); - $ids = array_keys($this->loadAutocompleteSearchByIndex($index->id())); - if ($ids) { - $controller = \Drupal::entityTypeManager() - ->getStorage('search_api_autocomplete_search'); - $entities = $controller->loadMultiple($ids); - $controller->delete($entities); - drupal_set_message($this->t('All autocompletion settings stored for this index were deleted.')); - } - else { - drupal_set_message($this->t('There were no settings to delete.'), 'warning'); - } - $form_state->setRedirectUrl($index->toUrl()); + /** + * Returns available suggesters for an index. + * + * @param \Drupal\search_api\IndexInterface $index + * The index to filter by. + * + * @return array[] + * An array of suggester plugin definitions. + */ + protected function getSuggestersForIndex(IndexInterface $index) { + $suggesters = array_map(function ($suggester_info) { + return $suggester_info['class']; + }, $this->suggesterManager->getDefinitions()); + $suggesters = array_filter($suggesters, function ($suggester_class) use ($index) { + return $suggester_class::supportsIndex($index); + }); + return $suggesters; + } + + /** + * @param string $index_id + * The index ID. + * + * @return \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface[] + */ + protected function loadAutocompleteSearchByIndex($index_id) { + return $this->entityTypeManager->getStorage('search_api_autocomplete_search')->loadByProperties([ + 'index_id' => $index_id, + ]); } } diff --git a/src/Form/AutocompleteSearchEditForm.php b/src/Form/AutocompleteSearchEditForm.php index 4f89420..4a096f8 100644 --- a/src/Form/AutocompleteSearchEditForm.php +++ b/src/Form/AutocompleteSearchEditForm.php @@ -2,35 +2,73 @@ namespace Drupal\search_api_autocomplete\Form; +use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginFormInterface; use Drupal\search_api\Form\SubFormState; use Drupal\search_api\IndexInterface; - +use Drupal\search_api_autocomplete\Type\TypeManager; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Provides an edit for for autocomplete search entities. + * + * @see \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface + */ class AutocompleteSearchEditForm extends EntityForm { - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch */ + /** + * The entity. + * + * @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface + */ protected $entity; /** - * @return \Drupal\Component\Plugin\PluginManagerInterface + * The autocomplete suggester manager. + * + * @var \Drupal\Component\Plugin\PluginManagerInterface + */ + protected $suggesterManager; + + /** + * The autocomplete type manager. + * + * @var \Drupal\search_api_autocomplete\Type\TypeManager + */ + protected $typeManager; + + /** + * Creates a new AutocompleteSearchEditForm instance. + * + * @param \Drupal\Component\Plugin\PluginManagerInterface $suggester_manager + * The suggester manager. + * @param \Drupal\search_api_autocomplete\Type\TypeManager $autocomplete_type_manager + * The autocomplete type manager. */ - protected function getAutocompleteTypeManager() { - return \Drupal::service('plugin_manager.search_api_autocomplete_type'); + public function __construct(PluginManagerInterface $suggester_manager, TypeManager $autocomplete_type_manager) { + $this->suggesterManager = $suggester_manager; + $this->typeManager = $autocomplete_type_manager; } /** - * @return \Drupal\Component\Plugin\PluginManagerInterface + * {@inheritdoc} */ - protected function getSuggesterManager() { - return \Drupal::service('plugin_manager.search_api_autocomplete_suggester'); + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin_manager.search_api_autocomplete_suggester'), + $container->get('plugin_manager.search_api_autocomplete_type') + ); } + /** + * + */ protected function getSuggestersForIndex(IndexInterface $index) { $suggesters = array_map(function ($suggester_info) { return $suggester_info['class']; - }, $this->getSuggesterManager()->getDefinitions()); + }, $this->suggesterManager->getDefinitions()); $suggesters = array_filter($suggesters, function ($suggester_class) use ($index) { return $suggester_class::supportsIndex($index); }); @@ -41,9 +79,9 @@ class AutocompleteSearchEditForm extends EntityForm { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - $form = parent::buildForm($form, $form_state); - $search = $search_api_autocomplete_settings = $this->entity; - $form['#title'] = t('Configure autocompletion for %search', ['%search' => $search_api_autocomplete_settings->label()]); + $form = parent::buildForm($form, $form_state); + $search = $search_api_autocomplete_search = $this->entity; + $form['#title'] = $this->t('Configure autocompletion for %search', ['%search' => $search_api_autocomplete_search->label()]); // If this is a re-build (i.e., most likely an AJAX call due to a new // suggester being selected), prepare the suggester sub-form state @@ -59,9 +97,8 @@ class AutocompleteSearchEditForm extends EntityForm { } } - /** @var \Drupal\search_api_autocomplete\AutocompleteTypeInterface $type */ - $type = $this->getAutocompleteTypeManager() - ->createInstance($search->getType()); + /** @var \Drupal\search_api_autocomplete\Type\TypeInterface $type */ + $type = $this->typeManager->createInstance($search->getType()); $form_state->set(['type'], $type); if (!$type) { drupal_set_message(t('No information about the type of this search was found.'), 'error'); @@ -75,8 +112,8 @@ class AutocompleteSearchEditForm extends EntityForm { ]; $form['suggester_id'] = [ '#type' => 'radios', - '#title' => t('Suggester plugin'), - '#description' => t('Choose the suggester implementation to use.'), + '#title' => $this->t('Suggester plugin'), + '#description' => $this->t('Choose the suggester implementation to use.'), '#options' => [], '#required' => TRUE, '#default_value' => $selected_suggester_id, @@ -87,13 +124,13 @@ class AutocompleteSearchEditForm extends EntityForm { ]; foreach ($this->getSuggestersForIndex($search->index()) as $suggester_id => $definition) { - // Load the suggester plugin. If the suggester is unchanged from the one on - // the saved version of the search, use the saved configuration. + // Load the suggester plugin. If the suggester is unchanged from the one + // on the saved version of the search, use the saved configuration. $configuration = []; if ($suggester_id == $search->getSuggesterId() && $search->getOption('suggester_configuration')) { $configuration = $search->getOption('suggester_configuration'); } - $suggester = $this->getSuggesterManager()->createInstance($suggester_id, [ + $suggester = $this->suggesterManager->createInstance($suggester_id, [ 'search' => $search, ] + $configuration); if (!$suggester) { @@ -112,7 +149,7 @@ class AutocompleteSearchEditForm extends EntityForm { if ($suggester_form) { $form['options']['suggester_configuration'] = $suggester_form; $form['options']['suggester_configuration']['#type'] = 'fieldset'; - $form['options']['suggester_configuration']['#title'] = t('Configure the %suggester suggester plugin', array('%suggester' => $suggester->label())); + $form['options']['suggester_configuration']['#title'] = $this->t('Configure the %suggester suggester plugin', array('%suggester' => $suggester->label())); $form['options']['suggester_configuration']['#description'] = $suggester->getDescription(); $form['options']['suggester_configuration']['#collapsible'] = TRUE; } @@ -138,16 +175,15 @@ class AutocompleteSearchEditForm extends EntityForm { $form['options']['min_length'] = [ '#type' => 'textfield', - '#title' => t('Minimum length of keywords for autocompletion'), - '#description' => t('If the entered keywords are shorter than this, no autocomplete suggestions will be displayed.'), + '#title' => $this->t('Minimum length of keywords for autocompletion'), + '#description' => $this->t('If the entered keywords are shorter than this, no autocomplete suggestions will be displayed.'), '#default_value' => $search->getOption('min_length', 1), '#validate' => ['element_validate_integer_positive'], ]; $form['options']['results'] = [ '#type' => 'checkbox', - '#title' => t('Display result count estimates'), - '#description' => t('Display the estimated number of result for each suggestion. ' . - 'This option might not have an effect for some servers or types of suggestion.'), + '#title' => $this->t('Display result count estimates'), + '#description' => $this->t('Display the estimated number of result for each suggestion. This option might not have an effect for some servers or types of suggestion.'), '#default_value' => (bool) $search->getOption('results', FALSE), ]; @@ -159,22 +195,22 @@ class AutocompleteSearchEditForm extends EntityForm { $form['advanced'] = [ '#type' => 'fieldset', - '#title' => t('Advanced settings'), + '#title' => $this->t('Advanced settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, ]; $form['advanced']['submit_button_selector'] = [ '#type' => 'textfield', - '#title' => t('Search button selector'), - '#description' => t('jQuery selector to identify the search button in the search form. Use the ID attribute of the "Search" submit button to prevent issues when another button is present (e.g., "Reset"). The selector is evaluated relative to the form. The default value is ":submit".', ['@jquery_url' => 'https://api.jquery.com/category/selectors/']), + '#title' => $this->t('Search button selector'), + '#description' => $this->t('jQuery selector to identify the search button in the search form. Use the ID attribute of the "Search" submit button to prevent issues when another button is present (e.g., "Reset"). The selector is evaluated relative to the form. The default value is ":submit".', ['@jquery_url' => 'https://api.jquery.com/category/selectors/']), '#default_value' => $search->getOption('submit_button_selector', ':submit'), '#required' => TRUE, '#parents' => ['options', 'submit_button_selector'], ]; $form['advanced']['autosubmit'] = [ '#type' => 'checkbox', - '#title' => t('Enable auto-submit'), - '#description' => t('When enabled, the search form will automatically be submitted when a selection is made by pressing "Enter".'), + '#title' => $this->t('Enable auto-submit'), + '#description' => $this->t('When enabled, the search form will automatically be submitted when a selection is made by pressing "Enter".'), '#default_value' => $search->getOption('autosubmit', TRUE), '#parents' => ['options', 'autosubmit'], ]; @@ -185,7 +221,7 @@ class AutocompleteSearchEditForm extends EntityForm { /** * Form AJAX handler for search_api_autocomplete_admin_search_edit(). */ - function search_api_autocomplete_suggester_ajax_callback(array $form, array &$form_state) { + public function search_api_autocomplete_suggester_ajax_callback(array $form, array &$form_state) { return $form['options']['suggester_configuration']; } @@ -204,14 +240,14 @@ class AutocompleteSearchEditForm extends EntityForm { if (!empty($values['options']['suggester_configuration'])) { $configuration = $values['options']['suggester_configuration']; } - $suggester = $this->getSuggesterManager()->createInstance($values['suggester_id'], ['search' => $this->entity] + $configuration); + $suggester = $this->suggesterManager->createInstance($values['suggester_id'], ['search' => $this->entity] + $configuration); $suggester_form = $form['options']['suggester_configuration']; unset($suggester_form['old_suggester_id']); $suggester_form_state = new SubFormState($form_state, ['options', 'suggester_configuration']); $suggester->validateConfigurationForm($suggester_form, $suggester_form_state); } - /** @var \Drupal\search_api_autocomplete\AutocompleteTypeInterface $type */ + /** @var \Drupal\search_api_autocomplete\Type\TypeInterface $type */ $type = $form_state->get('type'); if ($type instanceof PluginFormInterface) { $custom_form = empty($form['options']['custom']) ? [] : $form['options']['custom']; @@ -220,8 +256,10 @@ class AutocompleteSearchEditForm extends EntityForm { } } + /** + * {@inheritdoc} + */ public function buildEntity(array $form, FormStateInterface $form_state) { - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $entity */ $search = $this->entity; $values = &$form_state->getValues(); @@ -240,7 +278,6 @@ class AutocompleteSearchEditForm extends EntityForm { return $search; } - /** * Submit callback for search_api_autocomplete_admin_search_edit(). * @@ -259,8 +296,7 @@ class AutocompleteSearchEditForm extends EntityForm { $search = $this->entity; // @fixme -// $form_state['redirect'] = 'admin/config/search/search_api/index/' . $search->index_id . '/autocomplete'; - + // $form_state['redirect'] = 'admin/config/search/search_api/index/' . $search->index_id . '/autocomplete'; // Allow the suggester to decide how to save its configuration. If the user // has disabled JS in the browser, or AJAX didn't work for some other reason, // a different suggester might be selected than that which created the config @@ -271,7 +307,7 @@ class AutocompleteSearchEditForm extends EntityForm { if (!empty($values['options']['suggester_configuration'])) { $configuration = $values['options']['suggester_configuration']; } - $suggester = $this->getSuggesterManager()->createInstance($values['suggester_id'], [ + $suggester = $this->suggesterManager->createInstance($values['suggester_id'], [ 'search' => $search, ] + $configuration); $suggester_form = $form['options']['suggester_configuration']; diff --git a/src/Permissions.php b/src/Permissions.php index a3fcc41..d4183f5 100644 --- a/src/Permissions.php +++ b/src/Permissions.php @@ -7,6 +7,9 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; +/** + * Provides permissions of the search_api_autocomplete module. + */ class Permissions implements ContainerInjectionInterface { use StringTranslationTrait; @@ -28,16 +31,23 @@ class Permissions implements ContainerInjectionInterface { $this->storage = $storage; } + /** + * {@inheritdoc} + */ public static function create(ContainerInterface $container) { return new static( - $container->get('entity_type.manager')->getStorage('search_api_autocomplete_settings') + $container->get('entity_type.manager')->getStorage('search_api_autocomplete_search') ); } - + /** + * Returns a list of permission, one per configured search. + * + * @return array[] + */ public function bySearch() { $perms = []; - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ foreach ($this->storage->loadMultiple() as $id => $search) { $perms['use search_api_autocomplete for ' . $id] = [ 'title' => $this->t('Use autocomplete for the %search search', ['%search' => $search->label()]), diff --git a/src/Plugin/search_api_autocomplete/AutocompleteType/Page.php b/src/Plugin/search_api_autocomplete/AutocompleteType/Page.php index 13c406b..80d394f 100644 --- a/src/Plugin/search_api_autocomplete/AutocompleteType/Page.php +++ b/src/Plugin/search_api_autocomplete/AutocompleteType/Page.php @@ -1,24 +1,65 @@ Search pages module."), * provider = "search_api_page", * ) */ -class Page extends PluginBase implements AutocompleteTypeInterface { +class Page extends PluginBase implements TypeInterface, ContainerFactoryPluginInterface { + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * Creates a new Page instance. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager') + ); + } /** * {@inheritdoc} @@ -39,7 +80,7 @@ class Page extends PluginBase implements AutocompleteTypeInterface { */ public function listSearches(IndexInterface $index) { $ret = array(); - $storage = \Drupal::entityTypeManager()->getStorage('search_api_page'); + $storage = $this->entityTypeManager->getStorage('search_api_page'); foreach ($storage->loadByProperties(['index' => $index->id()]) as $page) { $id = 'search_api_page_' . $page->id(); $ret[$id]['name'] = $page->label(); @@ -51,7 +92,7 @@ class Page extends PluginBase implements AutocompleteTypeInterface { /** * {@inheritdoc} */ - public function createQuery(SearchApiAutocompleteSearch $search, $complete, $incomplete) { + public function createQuery(SearchApiAutocompleteSearchInterface $search, $complete, $incomplete) { $page = SearchApiPage::load($search->getOption('custom.page_id')); // Copied from search_api_page_search_execute(). $query = Utility::createQuery(Index::load($page->getIndex())); diff --git a/src/Plugin/search_api_autocomplete/AutocompleteType/Views.php b/src/Plugin/search_api_autocomplete/AutocompleteType/Views.php index 9fdb314..a42feb4 100644 --- a/src/Plugin/search_api_autocomplete/AutocompleteType/Views.php +++ b/src/Plugin/search_api_autocomplete/AutocompleteType/Views.php @@ -9,19 +9,20 @@ use Drupal\Core\Plugin\PluginFormInterface; use Drupal\Core\Url; use Drupal\search_api\IndexInterface; use Drupal\search_api\SearchApiException; -use Drupal\search_api_autocomplete\AutocompleteTypeInterface; -use Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch; +use Drupal\search_api_autocomplete\Type\TypeInterface; +use Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface; use Drupal\views\Views as ViewsViews; /** - * @AutocompleteType( + * @SearchapiAutocompleteType( * id = "views", * label = @Translation("Search views"), * description = @Translation("Searches provided by views"), * provider = "search_api", + * deriver = "Drupal\search_api_autocomplete\Plugin\Derivative\ViewsAutocompleteType" * ) */ -class Views extends PluginBase implements AutocompleteTypeInterface, ConfigurablePluginInterface, PluginFormInterface { +class Views extends PluginBase implements TypeInterface, ConfigurablePluginInterface, PluginFormInterface { /** * {@inheritdoc} @@ -64,7 +65,7 @@ class Views extends PluginBase implements AutocompleteTypeInterface, Configurabl * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ $search = $form_state->getFormObject()->getEntity(); $views_id = substr($search->id(), 17); $view = ViewsViews::getView($views_id); @@ -81,7 +82,7 @@ class Views extends PluginBase implements AutocompleteTypeInterface, Configurabl "Note: Autocompletion doesn't work well with contextual filters. Please see the README.txt file for details.", [':readme_url' => Url::fromUri('base://' . drupal_get_path('module', 'search_api_autocomplete') . '/README.txt')->toString()]), '#options' => $options, - '#default_value' => $search->getOption('custom.display') ? : 'default', + '#default_value' => $search->getOption('custom.display') ?: 'default', ]; return $form; @@ -97,7 +98,7 @@ class Views extends PluginBase implements AutocompleteTypeInterface, Configurabl * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - /** @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search */ + /** @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search */ $search = $form_state->getFormObject()->getEntity(); $views_id = substr($search->id(), 17); $view = ViewsViews::getView($views_id); @@ -129,7 +130,7 @@ class Views extends PluginBase implements AutocompleteTypeInterface, Configurabl /** * {@inheritdoc} */ - public function createQuery(SearchApiAutocompleteSearch $search, $complete, $incomplete) { + public function createQuery(SearchApiAutocompleteSearchInterface $search, $complete, $incomplete) { $views_id = substr($search->id(), 17); $view = ViewsViews::getView($views_id); if (!$view) { @@ -145,7 +146,7 @@ class Views extends PluginBase implements AutocompleteTypeInterface, Configurabl $vars['@view'] = $view->storage->label() ?: $views_id; throw new SearchApiException(t('Could not create query for view @view.', $vars)); } - // $query->setFulltextFields([$complete]); + // $query->setFulltextFields([$complete]);. // @todo What are the right values to use here? $query->setFulltextFields(); return $query; diff --git a/src/Plugin/search_api_autocomplete/AutocompleteSuggester/Server.php b/src/Plugin/search_api_autocomplete/suggester/Server.php similarity index 91% rename from src/Plugin/search_api_autocomplete/AutocompleteSuggester/Server.php rename to src/Plugin/search_api_autocomplete/suggester/Server.php index 3e4db90..57458c8 100644 --- a/src/Plugin/search_api_autocomplete/AutocompleteSuggester/Server.php +++ b/src/Plugin/search_api_autocomplete/suggester/Server.php @@ -1,12 +1,13 @@ options should be checked for settings, like whether @@ -58,6 +56,6 @@ interface SearchApiAutocompleteInterface extends BackendInterface { * An array of suggestions, as defined by * SearchApiAutocompleteSuggesterInterface::getAutocompleteSuggestions(). */ - public function getAutocompleteSuggestions(QueryInterface $query, SearchApiAutocompleteSearch $search, $incomplete_key, $user_input); + public function getAutocompleteSuggestions(QueryInterface $query, SearchApiAutocompleteSearchInterface $search, $incomplete_key, $user_input); } diff --git a/src/SearchApiAutocompleteSearchInterface.php b/src/SearchApiAutocompleteSearchInterface.php new file mode 100644 index 0000000..f888268 --- /dev/null +++ b/src/SearchApiAutocompleteSearchInterface.php @@ -0,0 +1,191 @@ +setCacheBackend($cache_backend, 'search_api_autocomplete_suggester'); $this->alterInfo('search_api_autocomplete_suggester'); diff --git a/src/Plugin/search_api_autocomplete/AutocompleteSuggester/SuggesterPluginBase.php b/src/Suggester/SuggesterPluginBase.php similarity index 79% rename from src/Plugin/search_api_autocomplete/AutocompleteSuggester/SuggesterPluginBase.php rename to src/Suggester/SuggesterPluginBase.php index 0fa3bc8..ffc0a5a 100644 --- a/src/Plugin/search_api_autocomplete/AutocompleteSuggester/SuggesterPluginBase.php +++ b/src/Suggester/SuggesterPluginBase.php @@ -6,18 +6,22 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\search_api\IndexInterface; use Drupal\search_api\Plugin\ConfigurablePluginInterface; -use Drupal\search_api_autocomplete\AutocompleteSuggesterInterface; -use Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch; +use Drupal\search_api_autocomplete\Suggester\SuggesterInterface; +use Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface; /** * Provides a base class for suggester plugins. + * + * @see \Drupal\search_api_autocomplete\Suggester\SuggesterInterface + * @see \Drupal\search_api_autocomplete\Suggester\SuggesterManager + * @see \Drupal\search_api_autocomplete\Annotation\SearchApiAutocompleteSuggester */ -abstract class SuggesterPluginBase extends PluginBase implements AutocompleteSuggesterInterface, ConfigurablePluginInterface { +abstract class SuggesterPluginBase extends PluginBase implements SuggesterInterface, ConfigurablePluginInterface { /** * The search this suggester is attached to. * - * @var \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch + * @var \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface */ protected $search; @@ -52,7 +56,7 @@ abstract class SuggesterPluginBase extends PluginBase implements AutocompleteSug /** * Constructs a SuggesterPluginBase object. * - * @param \Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch $search + * @param \Drupal\search_api_autocomplete\SearchApiAutocompleteSearchInterface $search * The search to which this suggester is attached. * @param array $configuration * An associative array containing the suggester's configuration, if any. @@ -61,7 +65,7 @@ abstract class SuggesterPluginBase extends PluginBase implements AutocompleteSug * @param array $plugin_definition * The suggester plugin's definition. */ - public function __construct(SearchApiAutocompleteSearch $search, array $configuration, $plugin_id, array $plugin_definition) { + public function __construct(SearchApiAutocompleteSearchInterface $search, array $configuration, $plugin_id, array $plugin_definition) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->search = $search; } diff --git a/src/Suggestion.php b/src/Suggestion.php new file mode 100644 index 0000000..42816e4 --- /dev/null +++ b/src/Suggestion.php @@ -0,0 +1,251 @@ +keys = $keys; + $this->url = $url; + $this->prefix = $prefix; + $this->suggestionPrefix = $suggestionPrefix; + $this->userInput = $userInput; + $this->suggestionSuffix = $suggestionSuffix; + $this->results = $results; + $this->render = $render; + } + + /** + * Creates a new suggestion from a string. + * + * @param string $suggestion + * The suggestion string. + * @param string $keys + * The search keys. + * + * @return static + */ + public static function fromString($suggestion, $keys) { + $pos = strpos($suggestion, $keys); + if ($pos === FALSE) { + return new static(NULL, NULL, '', '', '', $suggestion); + } + else { + return new static(NULL, '', '', substr($suggestion, 0, $pos), $keys, substr($suggestion, $pos + strlen($keys))); + } + } + + /** + * Creates a suggestion from a suggestion suffix. + * + * @param string $suggestion_suffix + * The suggestion suffix. + * @param int $results + * (optional) The amount of results. + * + * @return static + */ + public static function fromSuggestionSuffix($suggestion_suffix, $results = 0) { + return new static(NULL, '', '', $suggestion_suffix, '', '', $results); + } + + /** + * {@inheritdoc} + */ + public function getKeys() { + return $this->keys; + } + + /** + * {@inheritdoc} + */ + public function getUrl() { + return $this->url; + } + + /** + * {@inheritdoc} + */ + public function getPrefix() { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function getSuggestionPrefix() { + return $this->suggestionPrefix; + } + + /** + * {@inheritdoc} + */ + public function getUserInput() { + return $this->userInput; + } + + /** + * {@inheritdoc} + */ + public function getSuggestionSuffix() { + return $this->suggestionSuffix; + } + + /** + * {@inheritdoc} + */ + public function getResults() { + return $this->results; + } + + /** + * {@inheritdoc} + */ + public function getRender() { + return $this->render; + } + + /** + * {@inheritdoc} + */ + public function setKeys($keys) { + $this->keys = $keys; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setUrl($url) { + $this->url = $url; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) { + $this->prefix = $prefix; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSuggestionPrefix($suggestionPrefix) { + $this->suggestionPrefix = $suggestionPrefix; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setUserInput($userInput) { + $this->userInput = $userInput; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSuggestionSuffix($suggestionSuffix) { + $this->suggestionSuffix = $suggestionSuffix; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setResults($results) { + $this->results = $results; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setRender($render) { + $this->render = $render; + return $this; + } + +} diff --git a/src/SuggestionInterface.php b/src/SuggestionInterface.php new file mode 100644 index 0000000..3c54d5e --- /dev/null +++ b/src/SuggestionInterface.php @@ -0,0 +1,161 @@ +setCacheBackend($cache_backend, 'search_api_autocomplete_type'); $this->alterInfo('search_api_autocomplete_type'); diff --git a/tests/src/Kernel/SearchCrudTest.php b/tests/src/Kernel/SearchCrudTest.php index 3146170..5bab6c4 100644 --- a/tests/src/Kernel/SearchCrudTest.php +++ b/tests/src/Kernel/SearchCrudTest.php @@ -17,6 +17,9 @@ class SearchCrudTest extends KernelTestBase { */ public static $modules = ['search_api_autocomplete', 'search_api', 'user']; + /** + * + */ public function testCreate() { $autocomplete_search = SearchApiAutocompleteSearch::create([ 'id' => 'muh',