laravel默认不启用postgresql原生语义,数组、jsonb、全文搜索、范围类型、hstore、几何类型等高级特性必须显式使用whereraw、db::raw或扩展包,否则 silently 退化为字符串处理。

直接说结论:Laravel 默认不启用 PostgreSQL 的原生语义,数组、JSONB、全文搜索、范围类型、hstore、几何类型等高级特性必须显式介入原生语法或扩展包,否则会 silently 退化为字符串处理。
PostgreSQL 数组字段必须用 whereRaw + @> / && / ANY
Laravel 的 where() 对 text[] 或 integer[] 字段做等值比较时,实际执行的是字符串匹配(如 '{a,b,c}' = ?),不是数组语义。结果是查不到、误匹配、SQL 报错。
- 查「包含某个值」(OR 逻辑):用
whereRaw('tags && ARRAY[?]', ['news']) - 查「同时包含多个值」(AND 逻辑):用
whereRaw('permissions @> ARRAY[?]', [['read', 'write']]),注意传二维数组 - 查「等于某数组」:必须用
whereRaw('roles = ARRAY[?]', [['admin', 'editor']]),不能用where('roles', ['admin', 'editor']) - 安全绑定参数只支持
ANY形式:例如whereRaw('? = ANY(tags)', ['blog']),避免 SQL 注入
JSONB 查询不能依赖 $casts,深层路径必须用 ->> 和 @>
虽然 $casts = ['meta' => 'array'] 能让读写 JSON 字段自动序列化,但无法支持 meta->preferences->theme = 'dark' 这类路径查询,更不能走 GIN 索引。
- 查 JSONB 字段是否存在键:
whereRaw('meta ? \'email\'') - 查字符串值匹配(带索引):
whereRaw('meta->>\'status\' = ?', ['active']) - 查 JSONB 数组是否包含值:
whereRaw('meta->\'tags\' @> ?', ['["urgent"]']) - 更新嵌套字段:
DB::statement("UPDATE posts SET meta = jsonb_set(meta, '{preferences,theme}', '\"light\"') WHERE id = ?", [$id])
全文搜索必须手动建 tsvector 列 + GIN 索引 + @@ 操作符
Eloquent 的 whereLike() 或 Scout 默认驱动(algolia/meilisearch)完全绕过 PostgreSQL 的全文能力。不建 tsvector 列、不加 GIN 索引、不用 @@,就等于没用 PostgreSQL 的搜索。
- 迁移中添加列和索引:
DB::unprepared("ALTER TABLE articles ADD COLUMN content_search tsvector; CREATE INDEX idx_articles_search ON articles USING GIN(content_search);") - 更新向量(推荐在模型
saving事件里):to_tsvector('english', coalesce(title, '') || ' ' || coalesce(body, '')) - 搜索时写死配置名:
whereRaw('content_search @@ plainto_tsquery(\'english\', ?)', [$keyword]) - 如果要用多语言,
'english'得换成对应配置名,且该配置需已存在(SELECT cfgname FROM pg_ts_config查)
范围类型(daterange/numrange)必须用 DB::raw 或自定义 Cast
PostgreSQL 的 daterange 字段被 Laravel 默认转成字符串(如 ["2023-01-01","2023-12-31")),导致 contains、overlaps 等运算全部失效。
- 查时间点是否在范围内:
whereRaw('?::date - 查两个范围是否重叠:
whereRaw('valid_period && ?::daterange', ["[2023-06-01,2023-09-01)"]) - 若想在模型属性中直接用 PHP
DatePeriod,必须写自定义Cast类,实现get/set方法解析[)语法并转对象 -
whereBetween()对daterange完全无效——它生成的是BETWEEN x AND y,不是范围操作符
最容易被忽略的一点:所有这些原生操作符(@>、&&、、<code>@@)都**不走 Eloquent 的 query builder 链式调用抽象层**,意味着你没法用 when() 或 tap() 自然包裹它们;一旦混用,容易漏掉 whereRaw 的参数绑定,引发注入或类型错误。











