Laravel Eloquent 查询 JSON 数组字段中特定索引的值
在 Laravel 项目开发中,我们经常会遇到数据库字段存储 JSON 格式数据的情况,其中 JSON 数组是比较常见的结构。当我们需要查询 JSON 数组字段中特定索引位置的值,或者根据特定索引的值筛选数据时,Eloquent 提供了便捷的查询方式,不需要手动解析 JSON 后再处理。下面我们就结合具体场景,介绍几种常用的查询方法。
场景说明
假设我们有一个 articles 表,其中有一个 tags 字段,存储的是 JSON 格式的数组,结构类似 ["科技", "编程", "后端"],索引从 0 开始。现在我们需要查询 tags 数组中索引为 1 的元素等于 "编程" 的所有文章,或者需要获取每条记录的 tags 数组索引 1 对应的值。
方法一:使用 whereJsonContains 查询特定索引值
如果数据库支持 JSON 函数(比如 MySQL 5.7+、PostgreSQL 等),可以直接使用 Eloquent 的 whereJsonContains 方法,结合 JSON 路径语法来指定数组索引。不同数据库的 JSON 路径语法略有差异,下面以 MySQL 为例说明。
在 MySQL 中,JSON 数组的索引路径写法是 $.索引位置,比如要取数组第一个元素(索引 0)就是 $.0,第二个元素(索引 1)就是 $.1。
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
/**
* 查询 tags 数组索引为1的元素等于"编程"的文章
*/
public function index()
{
// MySQL 中 JSON 路径指定索引1的位置
$articles = Article::whereJsonContains('tags', '编程', '$.1')->get();
// 如果需要查询索引0的元素,只需要把路径改成 $.0
// $articles = Article::whereJsonContains('tags', '科技', '$.0')->get();
return view('articles.index', compact('articles'));
}
}需要注意的是,whereJsonContains 的第三个参数是可选的 JSON 路径,如果不传递则默认匹配整个 JSON 数组是否包含目标值,这里我们传递 $.1 就只匹配数组中索引为 1 的位置。
方法二:使用原生 JSON_EXTRACT 函数查询
如果上面的方法在你的数据库环境中不生效,也可以直接使用 Eloquent 的 whereRaw 方法,调用数据库原生的 JSON 提取函数来实现。还是以 MySQL 为例,MySQL 提供了 JSON_EXTRACT 函数来提取 JSON 字段中的值。
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
/**
* 使用原生 JSON_EXTRACT 查询 tags 数组索引1的值
*/
public function index()
{
// MySQL 中 JSON_EXTRACT 提取索引1的值,注意返回的JSON字符串带引号,需要去掉
$articles = Article::whereRaw("JSON_UNQUOTE(JSON_EXTRACT(tags, '$[1]')) = ?", ['编程'])->get();
return view('articles.index', compact('articles'));
}
}这里的 JSON_EXTRACT(tags, '$[1]') 会返回 "编程"(带双引号的字符串),所以需要用 JSON_UNQUOTE 函数去掉引号,再进行等值比较。如果是其他数据库,比如 PostgreSQL 可以使用 -> 操作符,例如 tags->1 就可以获取索引 1 的值。
方法三:获取特定索引的值并展示
如果我们不需要筛选,只是需要在查询后获取每条记录 JSON 数组特定索引的值,可以在模型访问器中处理,或者在查询后手动提取。
首先在 Article 模型中定义访问器:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $casts = [
'tags' => 'array', // 自动将 JSON 字段转为数组
];
/**
* 获取 tags 数组索引1的值
*/
public function getSecondTagAttribute()
{
// 因为已经 cast 为数组,直接通过数组索引获取
return $this->tags[1] ?? null;
}
}然后在控制器中就可以直接调用这个访问器:
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function show($id)
{
$article = Article::findOrFail($id);
// 直接获取第二个标签(索引1)
$secondTag = $article->second_tag;
return view('articles.show', compact('article', 'secondTag'));
}
}注意事项
- 不同数据库的 JSON 函数支持不同,使用前请确认你的数据库版本是否支持对应的 JSON 操作。
- 如果 JSON 数组的长度可能小于你要查询的索引,需要做空值判断,避免出现未定义索引的错误。
- 使用
$casts属性将 JSON 字段转为数组后,操作起来会更方便,建议优先使用这种方式处理 JSON 字段。