ThinkPHP 5.1 集成 Elasticsearch 搜索需满足四点:① 使用 elasticsearch/elasticsearch:^7.0 客户端且 PHP ≥7.2;② 启用 ext-curl 和 ext-json 扩展;③ ES 服务端安装 analysis-ik 插件并创建带 ik_smart 分词器的索引 mapping;④ 搜索时显式指定 analyzer=ik_smart。

在 ThinkPHP 5.1 项目中启用 Elasticsearch 搜索功能,但搜索结果始终为空、报错 Class 'Elasticsearch\ClientBuilder' not found 或中文字段搜不到关键词,问题根源往往不是代码写错,而是客户端版本错配、PHP 扩展缺失、索引 mapping 未预设 IK 分词器,或服务端监听地址未开放。
确认 PHP 环境与扩展状态
ThinkPHP 5.1 + Elasticsearch 必须使用 elasticsearch/elasticsearch:^7.0 客户端,它要求 PHP 版本 ≥7.2 且必须启用 ext-curl 和 ext-json。
执行 php -v 查看当前 PHP 版本,若低于 7.2,请先升级或切换环境。
运行 php -m | grep curl 和 php -m | grep json,两个命令都必须有输出;若无 curl 输出,打开 php.ini,取消 extension=curl 前的分号,重启 PHP-FPM 或 Apache。
立即学习“PHP免费学习笔记(深入)”;
【ext-curl 未启用会导致 ClientBuilder 类自动加载失败,即使 Composer 安装成功,运行时仍报 “Class not found”】
安装兼容的 Elasticsearch PHP 客户端
进入 ThinkPHP 5.1 项目根目录,执行以下命令:
composer require elasticsearch/elasticsearch:^7.0
该版本兼容 Elasticsearch 7.x(如 7.14.1)和 6.8,禁止使用 ^6.0 或更低版本——官方已弃用,且与 TP5.1 的 autoload 机制存在冲突。
安装完成后,检查 vendor/elasticsearch/elasticsearch/ 目录是否存在,再运行验证命令:
php -r "echo class_exists('Elasticsearch\ClientBuilder') ? 'ok' : 'fail';",输出 ok 才表示类已正确加载。
配置连接并初始化客户端
方法一:在控制器中直接初始化(适合快速验证)
在控制器顶部引入:use Elasticsearch\ClientBuilder;
构造客户端实例时,hosts 只接受 host:port 格式,不能带协议或路径,例如:['127.0.0.1:9200'] ✅,而 ['http://localhost:9200/es'] ❌ 会触发连接失败。
若 Elasticsearch 启用了 X-Pack 安全认证(默认开启),必须传入用户名密码:
$client = ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->setBasicAuthentication('elastic', 'your_password')->build();
方法二:封装为独立服务类(推荐用于生产)
在 app/common/service/EsService.php 中创建服务类,构造函数内完成 client 初始化,并加 try-catch 捕获连接异常;避免把 client 实例直接塞进 __construct() 就返回,需确保 build() 成功后再使用。
注意:TP5.1 不支持 Laravel 风格的服务容器自动绑定,需手动 new 或通过单例模式调用。
创建索引并设置中文分词 mapping
第一步:确认 Elasticsearch 服务器已安装 analysis-ik 插件。
登录 ES 服务器,执行 bin/elasticsearch-plugin list,输出中必须含 analysis-ik;若无,需下载对应 ES 版本的 IK 包(如 ES 7.14.1 → IK 7.14.1),解压至 plugins/ik/ 目录后重启服务。
第二步:显式创建索引并指定 ik_smart 分词器,否则中文字段将按单字切分,无法匹配词语。
执行以下 mapping 定义(以 goods_es 索引为例):
$params = [
'index' => 'goods_es',
'body' => [
'settings' => ['number_of_shards' => 3, 'number_of_replicas' => 1],
'mappings' => [
'properties' => [
'title' => ['type' => 'text', 'analyzer' => 'ik_smart'],
'content' => ['type' => 'text', 'analyzer' => 'ik_smart']
]
]
]
];
$client->indices()->create($params);
【跳过这步直接插入文档,中文搜索必然失败——ES 默认 standard 分词器对中文无效】
同步数据到 Elasticsearch
① 在模型的 afterWrite 钩子中主动推送新增/更新记录:
在 app/model/Goods.php 中定义:
protected function afterWrite($data) {
if ($data['for_au'] == 1) {
$es = \app\common\service\EsService::getInstance();
$es->indexDocument('goods_es', 'goods_es', $data['id'], ['title' => $data['product_name'], 'content' => $data['desc']]);
}
}
② 删除时调用 deleteDocument() 方法清除对应文档,避免脏数据残留。
③ 首次全量导入可单独写命令行脚本,用 Db::name('goods')->select() 批量写入,注意控制每次 bulk 数量 ≤ 1000,防止内存溢出。
执行中文搜索查询
构建 DSL 查询语句,必须指定 analyzer 为 ik_smart,否则查询词不会被正确分词:
$params = [
'index' => 'goods_es',
'body' => [
'query' => [
'multi_match' => [
'query' => '手机',
'fields' => ['title^3', 'content'],
'analyzer' => 'ik_smart'
]
]
]
];
$results = $client->search($params);
返回结果中 $results['hits']['hits'] 即为匹配文档数组,可直接渲染到前端。



















