在 yii2 中查询 mysql json 字段需先迁移设为 json 类型并重写 attributes() 注册字段,再用 expression 包裹 json_unquote(json_extract()) 等函数构造条件,或建生成列索引后按普通字段查询。

在 Yii2 中直接查询数据库 JSON 字段的特定键值(如 MySQL 5.7+ 的 title_json->"$.en-US"),需绕过 AR 默认限制,启用原生 JSON 函数支持并正确映射字段表达式,否则 where 条件会失效或报 SQL 错误。
启用 MySQL JSON 函数支持
Yii2 默认不识别 JSON_EXTRACT 等函数返回的虚拟列。必须在迁移中显式声明字段类型为 json,并在模型中重写 attributes() 方法,将 JSON 字段注册为“可读属性”:
第一步:执行迁移命令,确保字段类型为 JSON:
php yii migrate/create add_title_json_to_article → 编辑生成文件,写入:$this->addColumn('article', 'title_json', $this->json());
第二步:在模型类中覆盖 attributes(),否则 AR 不会把 title_json 当作合法属性参与构建 WHERE 条件:
public function attributes() { $attrs = parent::attributes(); $attrs[] = 'title_json'; return $attrs; }
【不加这一步,后续所有 ->where(['title_json' => ...]) 都会静默忽略该条件】
查询 JSON 键值的三种写法
方法一:用 JSON_UNQUOTE(JSON_EXTRACT()) + andWhere(兼容性最强)
适用于所有支持 JSON 的 MySQL 版本,且能走索引(若已建生成列索引):
$query = Article::find()->andWhere(['like', new \yii\db\Expression('JSON_UNQUOTE(JSON_EXTRACT(title_json, "$.en-US"))'), 'Admin']);
注意:必须用 new \yii\db\Expression 包裹,否则 Yii 会尝试转义引号导致语法错误。
方法二:用 JSON_CONTAINS() 做存在性匹配
适合模糊搜索整个 JSON 值片段,但无法精准匹配键内字符串:
$query = Article::find()->andWhere(new \yii\db\Expression("JSON_CONTAINS(title_json, '\"Admin\"', '$.\"en-US\"')"));
方法三:定义虚拟生成列并建立索引(仅 MySQL 5.7+)
先在迁移中添加生成列:$this->addColumn('article', 'title_en_us', 'VARCHAR(255) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(title_json, "$.en-US"))) STORED'); → 再建索引:$this->createIndex('idx_title_en_us', 'article', 'title_en_us');
之后即可像普通字段一样查询:Article::find()->where(['title_en_us' => 'Admin'])->one();
在 ActiveQuery 中封装 JSON 查询逻辑
避免每次手写 Expression,把常用 JSON 查询抽成 scope:
在 ArticleQuery 类中添加:
public function andFilterWhereTitleEnUs($value) { return $this->andWhere(['like', new \yii\db\Expression('JSON_UNQUOTE(JSON_EXTRACT(title_json, "$.en-US"))'), $value]); }
使用时直接链式调用:Article::find()->andFilterWhereTitleEnUs('Dashboard')->all();
这一步操作起来很简单,直接把方法塞进 Query 类就行,但必须确保 Query 类已通过 Article::find() 正确返回——如果没重写 Article::find() 指向自定义 Query 类,这个方法永远不会被调用。











