Laravel 中高效查询 JSON 字段的正确方法

8次阅读

Laravel 中高效查询 JSON 字段的正确方法

本文详解 laravel eloquent 和 query builder 中安全、兼容地查询 mysql/postgresql数据库 json 列(如 `info->id`)的最佳实践,纠正 `whereraw` 拼接导致的 sql 注入与语法错误,并介绍 `wherejsoncontains`、`wherejsonlength` 等高级 json 查询方法。

laravel 中对 JSON 类型字段(如 mysql 的 JSON 列或 PostgreSQL 的 jsonb)进行条件查询时,切勿直接拼接变量到 whereRaw 中——这不仅存在严重的 SQL 注入风险,还会因引号缺失或类型隐式转换导致报错(例如你遇到的 Unknown column ‘6112’ 错误)。该错误本质是 MySQL 将未加引号的数字 6112 误解析为列名而非字面值,根源在于 ->whereRaw(‘JSON_EXTRACT(info, “$.id”) = ‘.$value) 缺少参数绑定和字符串转义。

✅ 正确且推荐的方式是使用 Laravel 原生支持的 JSON 路径操作符 ->

$order = DB::table('orders')     ->where('info->id', $value) // ✅ 自动处理类型与转义,等价于 JSON_EXTRACT(info, '$.id')     ->first();

该语法在 Laravel 5.6+ 中原生支持,底层会根据数据库类型自动编译为对应函数:

  • MySQL → JSON_EXTRACT(“info”, “$.id”) = ?
  • PostgreSQL → “info”->>’id’ = ?
  • sqlite(启用 JSON1)→ json_extract(“info”, ‘$.id’) = ?

同时,它会通过 pdo 参数绑定安全传递 $value,彻底规避注入与语法错误。

? 进阶 JSON 查询场景(仅限 MySQL/PostgreSQL):

  • 匹配 JSON 数组中是否包含某值(如 info->tags 是 [“urgent”, “paid”]):

    $orders = DB::table('orders')     ->whereJsonContains('info->tags', 'urgent')     ->get();
  • 匹配多个值(AND 逻辑)

    // 查找同时包含 'urgent' 和 'paid' 的订单 $orders = DB::table('orders')     ->whereJsonContains('info->tags', ['urgent', 'paid'])     ->get();
  • 按 JSON 数组长度筛选

    // 查找 tags 数组长度大于 1 的订单 $orders = DB::table('orders')     ->whereJsonLength('info->tags', '>', 1)     ->get();

⚠️ 注意事项:

  • whereJsonContains 和 whereJsonLength 不支持 SQLite(除非手动启用并验证 JSON1 扩展);
  • JSON 路径区分大小写(如 ‘info->Id’ ≠ ‘info->id’);
  • 若 JSON 字段可能为 NULL,建议补充空值判断:->whereNotNull(‘info’)->where(‘info->id’, $value);
  • 在 Eloquent 模型中,可直接复用相同语法:
    $order = Order::where('info->id', $value)->first();

总结:始终优先使用 -> 操作符替代手写 JSON_EXTRACT + whereRaw;它更简洁、安全、跨库兼容,且由 Laravel 框架统一维护底层适配逻辑。

text=ZqhQzanResources