diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index 81c03dbca0..8850d40ef7 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -234,6 +234,9 @@ field.widget.settings.entity_reference_autocomplete_tags: match_operator: type: string label: 'Autocomplete matching' + match_limit: + type: integer + label: 'Maximum number of autocomplete suggestions.' size: type: integer label: 'Size of textfield' @@ -248,6 +251,9 @@ field.widget.settings.entity_reference_autocomplete: match_operator: type: string label: 'Autocomplete matching' + match_limit: + type: integer + label: 'Maximum number of autocomplete suggestions.' size: type: integer label: 'Size of textfield' diff --git a/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php index 2cede4dffa..93a8539ced 100644 --- a/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php +++ b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php @@ -61,7 +61,8 @@ public function getMatches($target_type, $selection_handler, $selection_settings if (isset($string)) { // Get an array of matching entities. $match_operator = !empty($selection_settings['match_operator']) ? $selection_settings['match_operator'] : 'CONTAINS'; - $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 10); + $match_limit = isset($selection_settings['match_limit']) ? (int) $selection_settings['match_limit'] : 10; + $entity_labels = $handler->getReferenceableEntities($string, $match_operator, $match_limit); // Loop through the entities and convert them into autocomplete output. foreach ($entity_labels as $values) { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php index 493d6a4f67..a407098f02 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php @@ -28,7 +28,8 @@ class EntityReferenceAutocompleteWidget extends WidgetBase { public static function defaultSettings() { return [ 'match_operator' => 'CONTAINS', - 'size' => '60', + 'match_limit' => 10, + 'size' => 60, 'placeholder' => '', ] + parent::defaultSettings(); } @@ -44,6 +45,13 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#options' => $this->getMatchOperatorOptions(), '#description' => t('Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of entities.'), ]; + $element['match_limit'] = [ + '#type' => 'number', + '#title' => t('Number of results'), + '#default_value' => $this->getSetting('match_limit'), + '#min' => 0, + '#description' => t('The number of suggestions that will be listed. Use 0 to remove the limit.'), + ]; $element['size'] = [ '#type' => 'number', '#title' => t('Size of textfield'), @@ -68,6 +76,7 @@ public function settingsSummary() { $operators = $this->getMatchOperatorOptions(); $summary[] = t('Autocomplete matching: @match_operator', ['@match_operator' => $operators[$this->getSetting('match_operator')]]); + $summary[] = t('Autocomplete suggestion list size: @size', ['@size' => $this->getSetting('match_limit') ?: 'unlimited']); $summary[] = t('Textfield size: @size', ['@size' => $this->getSetting('size')]); $placeholder = $this->getSetting('placeholder'); if (!empty($placeholder)) { @@ -88,7 +97,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $referenced_entities = $items->referencedEntities(); // Append the match operation to the selection settings. - $selection_settings = $this->getFieldSetting('handler_settings') + ['match_operator' => $this->getSetting('match_operator')]; + $selection_settings = $this->getFieldSetting('handler_settings') + [ + 'match_operator' => $this->getSetting('match_operator'), + 'match_limit' => $this->getSetting('match_limit'), + ]; $element += [ '#type' => 'entity_autocomplete', diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index b4e2b860a7..c08e722d6f 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay; +use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget; /** * Re-save all configuration entities to recalculate dependencies. @@ -190,3 +191,32 @@ function system_post_update_add_expand_all_items_key_in_system_menu_block(&$sand return strpos($block->getPluginId(), 'system_menu_block:') === 0; }); } + +/** + * Populate the new 'match_limit' setting for entity reference autocomplete widget. + */ +function system_post_update_entity_reference_autocomplete_match_limit(&$sandbox = NULL) { + $config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class); + /** @var \Drupal\Core\Field\WidgetPluginManager $field_widget_manager */ + $field_widget_manager = \Drupal::service('plugin.manager.field.widget'); + + $callback = function (EntityDisplayInterface $display) use ($field_widget_manager) { + $needs_save = FALSE; + foreach ($display->getComponents() as $field_name => $component) { + if (empty($component['type'])) { + continue; + } + + $plugin_definition = $field_widget_manager->getDefinition($component['type'], FALSE); + if (is_a($plugin_definition['class'], EntityReferenceAutocompleteWidget::class, TRUE)) { + $component['settings']['match_limit'] = 10; + $display->setComponent($field_name, $component); + $needs_save = TRUE; + } + } + + return $needs_save; + }; + + $config_entity_updater->update($sandbox, 'entity_form_display', $callback); +} diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.media.audio.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.media.audio.default.yml index 231e7f3bdb..ff4680498f 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.media.audio.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.media.audio.default.yml @@ -43,6 +43,7 @@ content: weight: 5 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.media.file.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.media.file.default.yml index a9fa8831c1..457d3b1a55 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.media.file.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.media.file.default.yml @@ -43,6 +43,7 @@ content: weight: 5 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.media.image.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.media.image.default.yml index fe6cfed79f..58b07fc576 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.media.image.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.media.image.default.yml @@ -45,6 +45,7 @@ content: weight: 5 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.media.remote_video.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.media.remote_video.default.yml index d0924b7042..69d5dab1e5 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.media.remote_video.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.media.remote_video.default.yml @@ -44,6 +44,7 @@ content: weight: 4 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.media.video.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.media.video.default.yml index b21333f87c..482935b5a7 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.media.video.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.media.video.default.yml @@ -43,6 +43,7 @@ content: weight: 5 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.node.article.default.yml index bc28702427..5e94f81013 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.node.article.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.node.article.default.yml @@ -47,6 +47,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } @@ -97,6 +98,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.node.page.default.yml index 76b8b9fe0b..71ab408f82 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.node.page.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.node.page.default.yml @@ -76,6 +76,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } diff --git a/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml b/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml index f1281c6283..6d0c211624 100644 --- a/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml +++ b/core/profiles/demo_umami/config/install/core.entity_form_display.node.recipe.default.yml @@ -78,6 +78,7 @@ content: weight: 6 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } @@ -103,6 +104,7 @@ content: weight: 7 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } @@ -154,6 +156,7 @@ content: weight: 12 settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' region: content diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml index 99d0f60827..b440edaf6f 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml @@ -53,6 +53,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } @@ -97,6 +98,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml index 682f1a550c..a7bb5b4c37 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml @@ -68,6 +68,7 @@ content: region: content settings: match_operator: CONTAINS + match_limit: 10 size: 60 placeholder: '' third_party_settings: { } diff --git a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php index d0b9c2953f..407e4cf37c 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php @@ -97,6 +97,30 @@ public function testEntityReferenceAutocompleteWidget() { $this->assertCount(1, $results); $assert_session->pageTextContains('Test page'); $assert_session->pageTextNotContains('Page test'); + + // Change the size of the result set. + entity_get_form_display('node', 'page', 'default') + ->setComponent($field_name, [ + 'type' => 'entity_reference_autocomplete', + 'settings' => [ + 'match_limit' => 1, + ], + ]) + ->save(); + + $this->drupalGet('node/add/page'); + $page = $this->getSession()->getPage(); + + $autocomplete_field = $page->findField($field_name . '[0][target_id]'); + $autocomplete_field->setValue('Test'); + $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + + $results = $page->findAll('css', '.ui-autocomplete li'); + + $this->assertCount(1, $results); + $assert_session->pageTextContains('Test page'); + $assert_session->pageTextNotContains('Page test'); } } diff --git a/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php b/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php index a44534a31f..78df2a7520 100644 --- a/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Rest/EntityFormDisplayResourceTestBase.php @@ -109,6 +109,7 @@ protected function getExpectedNormalizedEntity() { 'weight' => 5, 'settings' => [ 'match_operator' => 'CONTAINS', + 'match_limit' => 10, 'size' => 60, 'placeholder' => '', ],