Drupal 11:将搜索 API 搜索输入框转换为带 SVG 图标的按钮

Drupal 11:对搜索 API 搜索输入进行主题化

2026 年 1 月 4 日 - 阅读时长 12 分钟

在 Drupal 开发中,我在对搜索 API 表单进行主题化时,常见的一个需求是用放大镜图标替换普通的提交元素。执行这个操作不算难,但确实需要添加几个步骤来添加一个建议,以便可以使用自定义模板。

当我设置一个视图来针对搜索 API 索引执行搜索时,通常会为文本内容创建一个公开筛选器。视图会将其显示为一个可以嵌入到网站中的块。然而,这个块带有一个输入元素作为搜索按钮,并且无法将 SVG 图标注入到输入元素中。

通过将输入元素更改为按钮,我们就能够注入一个小的放大镜 SVG 或类似图标作为搜索按钮。

替换这个输入元素需要几个步骤,尽管我已经使用过几次这种技术,但每次还是需要翻找旧代码来弄清楚具体做法。所以,我想把它记录下来,这样之后就不用再四处寻找解决方案了。

在本文中,我将探讨如何结合使用表单修改和建议钩子,将搜索 API 表单的提交输入更改为按钮,以便可以在其中嵌入 SVG。这在 Drupal 模块开发中是一个比较实用的技巧。

一、修改搜索表单

第一步(可能也是最棘手的一步)是修改搜索表单,为搜索提交元素添加几个属性。

如果我们为输入元素添加一个主题建议修改钩子,该元素本身并不知道其周围的上下文。这就使得很难确定我们是否在修改正确的元素,甚至难以注入一个对搜索表单来说是唯一的建议。

因此,使用表单修改钩子将一个属性注入到表单元素中,这样我们就可以在建议钩子中读取这个属性。这为我们提供了一些可以识别并在建议钩子中使用的数据。

找到正确的表单有点棘手,因为搜索表单的名称在不同网站之间有所不同,并且取决于为显示搜索结果而设置的视图。我通常使用视图的名称来查找正确的表单,但也可以通过其他方式推断出正确的表单。

修改正确表单的最简单方法是在钩子中使用视图的名称。例如,如果视图名为“content_search”,那么表单的 #id 元素的 ID 将为“views-exposed-form-content-search-search-page”。我们可以使用这些信息以如下方式修改表单(使用新的面向对象编程钩子机制)。

namespace Drupal\mymodule\Hook;

use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

class SearchFormHooks {

  use StringTranslationTrait;

  public const SEARCH_VIEW_NAME = 'content-search';

  /**
   * Implements hook_form_alter().
   */
  #[Hook('form_alter')]
  public function formAlter(&$form, FormStateInterface $form_state, $form_id) {
    if ($form_id === 'views_exposed_form' && $form['#id'] === 'views-exposed-form-' . self::SEARCH_VIEW_NAME . '-search-page') {
      // Convert the search input to a button.
      $form['actions']['submit']['#attributes']['data-twig-suggestion'] = 'search_results_submit';
      $form['actions']['submit']['#attributes']['class'][] = 'search-box__button';
      $form['actions']['submit']['#attributes']['aria-label'][] = $this->t("Search");
    }
  }

}

视图的名称已作为常量添加,以方便将此钩子移植到其他网站(或搜索表单)。

搜索输入元素现在应该包含一个名为 data-twig-suggestion 的数据属性。

<input data-twig-suggestion="search_results_submit" class="search-box__button button js-form-submit form-submit" aria-label="Search" data-drupal-selector="edit-submit-content-search" type="submit" id="edit-submit-content-search" value="Search">

注意,我们还注入了一个自定义类和一个 aria-label 属性。该类用于一般的主题化目的,但 aria-label 对于屏幕阅读器至关重要。我们这里所做的是移除搜索的标签,并用 SVG 图标替换它。因此,我们需要添加这个标签,以确保屏幕阅读器仍然能够以正确的标签识别该按钮。

另一种使用表单 ID 的替代方法可能是查看表单的动作,对于搜索 API 系统,该动作通常是“/search”。例如,我们可以像这样重写钩子。

if ($form_id === 'views_exposed_form' && $form['#action'] === '/search') {
  // Convert the search input to a button.
  $form['actions']['submit']['#attributes']['data-twig-suggestion'] = 'search_results_submit';
  $form['actions']['submit']['#attributes']['class'][] = 'search-box__button';
  $form['actions']['submit']['#attributes']['aria-label'][] = t("Search");
}

如果上述步骤没有产生预期结果,你可能需要在你的设置中进行一些试验。

完成这些后,我们就可以添加模板建议钩子了。

二、添加建议钩子和模板

现在,输入元素的建议钩子可以专注于在属性数组中查找 data-twig-suggestion 属性。如果该属性存在,我们就知道是我们注入的,并且可以在建议中使用它。我们不会丢弃该属性,而是将其传递给建议。这样,如果将来需要,我们就能够根据值来调整建议。

以下面向对象编程钩子与我们上面定义的 formAlter() 方法放在同一个类中。

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
#[Hook('theme_suggestions_input_alter')]
public function themeHookSuggestion(&$suggestions, array $variables) {
    $element = $variables['element'];
    // Mainly used to swap the search input to a button.
    if (isset($element['#attributes']['data-twig-suggestion'])) {
      $suggestions[] = 'input__' . $element['#type'] . '__' . $element['#attributes']['data-twig-suggestion'];
    }
}

这会为模板创建一个名为 input--submit--search-results-submit.html.twig 的建议,我们可以将其添加到主题中。

可以添加输入元素的模板,基本代码如下。

{% set attributes = attributes.setAttribute('aria-label', 'Search'|trans) %}
<button{{ attributes }}>
    <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M16.4999 16.5L12.0354 12.0355" stroke="#211E1E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
        <path d="M8.5 13.5C11.2614 13.5 13.5 11.2614 13.5 8.5C13.5 5.73858 11.2614 3.5 8.5 3.5C5.73858 3.5 3.5 5.73858 3.5 8.5C3.5 11.2614 5.73858 13.5 8.5 13.5Z" stroke="#211E1E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
</button>

虽然没有这些属性表单也能正常工作,但最好还是包含它们,特别是因为我们在表单修改钩子中为搜索添加了 aria-label,并且需要显示该标签。

完成所有这些后,现在搜索表单使用的是按钮而不是输入元素。该按钮包含一个内联的放大镜 SVG,并且可以进一步修改以添加其他文本或元素。

我公司曾考虑将其作为一个贡献模块发布,但搜索表单的检测和最终的模板在不同网站之间差异相当大。在 Drupal 11 的升级过程中,这种对搜索 API 表单的优化也可能需要根据实际情况进行调整,这也体现了 Drupal 升级所涉及的细节工作。