You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Drupal 8站点外部API与内部内容的联合搜索?

在Drupal 8中实现内部内容+外部API内容的联合搜索

要实现同时检索Drupal内部基础页面和外部API文章的搜索功能,核心是自定义搜索逻辑,同时拉取两类数据源并整合结果,下面是具体实现方案:

一、核心思路

不用完全替换Drupal核心搜索,而是新增一个联合搜索的处理逻辑:接收用户搜索关键词后,分别查询Drupal内部内容和外部API,将两类结果标准化格式后统一渲染,同时保留权限校验、缓存等关键机制。

二、具体实现步骤

1. 创建自定义模块

先搭个基础模块(比如命名为combined_search),用来承载所有自定义搜索逻辑,避免修改核心或第三方模块。

2. 处理内部内容检索

利用Drupal的实体查询机制,快速筛选匹配关键词的内部节点(比如基础页面page类型),同时做权限校验:

// 获取用户输入的关键词
$keyword = \Drupal::request()->query->get('q');
$escaped_keyword = \Drupal\Database\Database::getConnection()->escapeLike($keyword);

// 查询发布状态、匹配关键词的基础页面
$query = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('status', 1)
  ->condition(
    \Drupal\Core\Database\Query\Condition::orConditionGroup()
      ->condition('title', "%{$escaped_keyword}%", 'LIKE')
      ->condition('body.value', "%{$escaped_keyword}%", 'LIKE')
  );
$nids = $query->execute();
$internal_nodes = \Drupal\node\Entity\Node::loadMultiple($nids);

// 过滤用户无权限查看的节点
$internal_results = [];
foreach ($internal_nodes as $node) {
  if ($node->access('view')) {
    $internal_results[] = [
      'type' => 'internal',
      'title' => $node->getTitle(),
      'url' => $node->toUrl()->toString(),
      'body' => strip_tags($node->get('body')->value),
      'created' => $node->getCreatedTime(),
    ];
  }
}

3. 调用外部API获取结果

用Drupal内置的Guzzle客户端请求外部API,处理请求失败的异常:

$external_results = [];
$client = \Drupal::httpClient();
try {
  $response = $client->get('https://your-external-api.com/search', [
    'query' => [
      'q' => $keyword,
      'limit' => 10, // 和内部结果数量保持一致
    ],
    'timeout' => 5,
  ]);
  $api_data = json_decode((string)$response->getBody(), TRUE);
  
  // 标准化外部结果格式,和内部结果对齐
  foreach ($api_data['items'] ?? [] as $item) {
    $external_results[] = [
      'type' => 'external',
      'title' => $item['title'],
      'url' => $item['link'],
      'body' => strip_tags($item['summary']),
      'created' => strtotime($item['publish_date']),
    ];
  }
} catch (\GuzzleHttp\Exception\RequestException $e) {
  // 记录日志,避免影响内部搜索结果展示
  \Drupal::logger('combined_search')->error('External API request failed: @msg', ['@msg' => $e->getMessage()]);
}

4. 整合并渲染结果

把内部和外部结果合并,可按发布时间排序,然后用自定义模板渲染:

// 合并结果并按发布时间倒序排序
$all_results = array_merge($internal_results, $external_results);
usort($all_results, function($a, $b) {
  return $b['created'] - $a['created'];
});

// 渲染自定义模板
return [
  '#theme' => 'combined_search_results',
  '#results' => $all_results,
  '#keyword' => $keyword,
];

同时在模块的.module文件中注册模板:

function combined_search_theme($existing, $type, $theme, $path) {
  return [
    'combined_search_results' => [
      'variables' => ['results' => [], 'keyword' => ''],
      'template' => 'combined-search-results',
    ],
  ];
}

模板文件templates/combined-search-results.html.twig示例:

{% if results %}
  <h2>搜索结果(关键词: {{ keyword }})</h2>
  <ul class="search-results">
    {% for result in results %}
      <li class="result-item {{ result.type }}">
        {% if result.type == 'external' %}
          <span class="badge">外部文章</span>
        {% else %}
          <span class="badge">本站页面</span>
        {% endif %}
        <h3><a href="{{ result.url }}">{{ result.title }}</a></h3>
        <p>{{ result.body|truncate(200) }}</p>
        <small>{{ result.created|format_date('medium') }}</small>
      </li>
    {% endfor %}
  </ul>
{% else %}
  <p>未找到匹配 "{{ keyword }}" 的内容</p>
{% endif %}

5. 配置搜索表单

修改默认搜索表单的提交路径,指向我们的联合搜索路由:

function combined_search_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if ($form_id == 'search_form') {
    $form['#action'] = \Drupal\Core\Url::fromRoute('combined_search.index')->toString();
  }
}

在routing.yml中定义路由:

combined_search.index:
  path: '/combined-search'
  defaults:
    _controller: '\Drupal\combined_search\Controller\SearchController::index'
    _title: '联合搜索'
  requirements:
    _permission: 'access content'

三、优化建议

  • 缓存外部结果:用Drupal的缓存API存储外部API返回的数据,比如\Drupal\Core\Cache\Cache::set("external_search_{$keyword}", $external_results, time() + 3600),减少API调用频次。
  • 分页支持:如果结果数量多,给内部查询和外部API都加上分页参数,合并后统一分页展示。
  • 相关性排序:内部内容可以用Drupal的搜索评分(如果启用核心搜索模块),外部API如果支持相关性排序就传递参数,提升结果精准度。

内容的提问来源于stack exchange,提问作者Steve James

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 04:26:39