别用 @highlight 做搜索高亮,因 Blade 编译时无法访问 Algolia 的 highlightResult,易致逻辑错位、XSS 风险和编译失败;应改用 Scout 原生 $post->highlight('field') 配合 {!! !!} 安全输出。

直接上结论:别用 @highlight 这种纯 Blade 指令做搜索高亮,它解决不了核心问题——Blade 编译阶段根本拿不到 Algolia 返回的 highlightResult 结构,硬写只会让逻辑错位、XSS 风险失控、模板编译失败。
为什么 @highlight($field) 不能直接封装高亮逻辑
Blade 指令本质是字符串替换,不是运行时 PHP 执行。你注册的 Blade::directive('highlight', ...) 回调里拿到的 $expression 是原始字符串(比如 'title'),不是已解析的字段值;更关键的是,它完全接触不到 Algolia 响应里的 highlightResult['title']['value'] —— 那个带 <mark> 的字符串只存在于搜索结果数组中,不在 Blade 上下文里。
- 试图在指令里调用
$post->highlight('title')?前提是模型得有这个方法,且 Scout 已启用高亮支持(->highlight()是 Scout 9.5+ 新增的辅助方法,不是 Blade 自带) - 直接拼
"<?php echo str_replace(...); ?>"?没用——你不知道原始关键词、不控制匹配边界、无法处理多词高亮、更没法还原highlightPreTag设置 - 把整个
$hits数组传进指令?Blade 不允许在@highlight中访问外部变量作用域,$expression只能是字面量或简单变量名,不能是嵌套数组访问
@highlight 指令在什么场景下反而会出问题
常见于开发者想“一步到位”封装高亮输出,结果埋下三类坑:
- 混淆数据来源:
@highlight($post->title)渲染的是原始字段,不是高亮后的 HTML,用户看到的仍是未加粗文本 - XSS 风险误判:用
{!! @highlight($post->content) !!}等同于直接输出用户输入内容,highlightResult本身不做过滤,<script>仍可执行 - 编译报错:若指令返回的 PHP 代码缺少分号、缩进错乱,或与周围
@if/@foreach块不匹配,Blade 编译器会静默生成语法错误的 PHP 文件,报错信息指向缓存路径而非原始 .blade.php
真正该做的:绕过 Blade 指令,改用 Scout 原生高亮 + 安全输出
高亮必须在搜索响应层面介入,而不是模板渲染阶段补救。正确路径是:
- 查询时显式启用高亮:
Post::search('laravel')->highlight(['title', 'content'])->get() - 确保模型的
toSearchableArray()返回字段名与highlight()参数一致(如返回'title',就 highlight'title') - Blade 中用
{!! $post->highlight('title') !!}输出——这个方法由 Scout 注入,内部已处理空值、转义和标签包裹 - 如果用的是原生 Algolia Client(非 Scout 封装),则必须手动从
$hit['highlightResult']['title']['value']取值,并确认attributesToRetrieve包含该字段
注意:$post->highlight() 返回的字符串已含 <mark>,所以必须用 {!! !!};但它不会自动过滤 XSS,若字段含用户可控内容(如评论),需提前在索引前 strip_tags 或白名单过滤。
如果非要封装成指令,只限极简安全场景
仅当满足全部条件时才考虑轻量封装:
- 字段内容绝对可信(如后台配置项、静态文案)
- 高亮关键词固定且已知(非动态搜索结果)
- 仅做简单
str_replace,不依赖highlightResult
示例(不推荐用于搜索结果):
Blade::directive('highlight', function ($expression) {
// $expression 示例:"'keyword', \$text"
return "<?php \$parts = explode(',', {$expression}, 2); \$kw = trim(\$parts[0], \"'\"); \$txt = isset(\$parts[1]) ? \$parts[1] : ''; echo str_replace(\$kw, '<mark>'.\$kw.'</mark>', \$txt); ?>";
});
用法:@highlight('laravel', $post->title)。但再次强调:这和 Algolia 高亮无关,只是个字符串替换玩具,别用在真实搜索页。
最易被忽略的一点:每次修改 Blade 指令后,必须运行 php artisan view:clear,否则旧编译缓存仍在生效,你改了十遍也看不到效果。


















