如何使用ThinkPHP模型修改器实现数组自动转换为JSON并存储?

2026-05-07 18:321阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计932个文字,预计阅读时间需要4分钟。

如何使用ThinkPHP模型修改器实现数组自动转换为JSON并存储?

在ThinkPHP模型中,直接给字段赋数值时,数据库中存储的仍然是原始的数据类型,而非字符串。如果模型中的字段类型声明为数值类型(如int、float等),则数据库存储的将是数值;如果声明为字符串类型(如varchar、text等),则存储的将是字符串。

类型声明只对读写属性生效,不涉及数据处理。例如:

为什么 $model->extra = ['a' => 1] 能转 JSON,但 $model->save(['extra' => ['a' => 1]]) 却存成 Array 字符串?

因为 ThinkPHP 的 JSON 自动编解码只在模型的属性访问器(__get/__set)中触发,而 save(['field' => $value]) 是绕过访问器、直写底层数据的。它把数组当普通值塞进 SQL 参数,PDO 默认把它转成 Array 字符串(如 "Array"),不是 JSON。

  • save(['extra' => ['a' => 1]]) → 数据库写入 "Array"(PHP 字符串强制转换结果)
  • $model->extra = ['a' => 1]; $model->save(); → 触发 setExtraAttr() → 自动 json_encode() → 存入 {"a":1}
  • 同理,$model->data(['extra' => ['a' => 1]])->save() 也绕过访问器,必须配合 allowField(true) + 前端传入已编码字符串,再手动 json_decode()

TP6 和 TP8 对 $json 属性的处理差异在哪?

TP6 要求你显式声明 protected $json = ['extra'];,否则字段永远是字符串;TP8 默认对数据库类型为 json 的字段启用自动 decode(目标为 array),但仅限于 find()/select() 查询结果——getData('extra')__get('extra') 仍可能返回原始字符串,取决于驱动是否注入 typecast。

  • TP6:不配 $json$model->extra 是字符串;配了 → 是数组(json_decode($value, true)
  • TP8:字段 DB 类型是 json → 查询结果中 $model->extra 默认是数组;但 $model->getData('extra') 返回未 decode 的原始字符串
  • 跨版本兼容写法:统一用 $model->extra 访问,避免混用 getData();写入一律走属性赋值,不用 save([...]) 直传数组

怎么安全地把用户提交的逗号分隔字符串转成 JSON 数组存库?

别碰 type => 'json'——那是给标准 JSON 字符串准备的。逗号分隔字符串属于非标格式,必须用自定义访问器,且要双向控制。

立即学习“PHP免费学习笔记(深入)”;

  • 数据库字段类型用 VARCHARTEXT,不要设成 JSON(MySQL 会拒绝非法值)
  • 模型里不写 $json,改用访问器:setTagsAttr() 接收字符串或数组,统一转成数组后 json_encode()getTagsAttr() 接收数据库原始字符串,json_decode()explode() 处理
  • 示例逻辑:

    public function setTagsAttr($value) { if (is_string($value) && !empty($value)) { $value = array_map('trim', explode(',', $value)); } return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } public function getTagsAttr($value) { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : ($value ? explode(',', $value) : []); }

最易被忽略的一点:JSON 字段内容修改后必须整体重新赋值才能持久化。$model->extra['name'] = 'new'; 不会触发变更检测,$model->save() 不会更新该字段——得写成 $data = $model->extra; $data['name'] = 'new'; $model->extra = $data; $model->save();

本文共计932个文字,预计阅读时间需要4分钟。

如何使用ThinkPHP模型修改器实现数组自动转换为JSON并存储?

在ThinkPHP模型中,直接给字段赋数值时,数据库中存储的仍然是原始的数据类型,而非字符串。如果模型中的字段类型声明为数值类型(如int、float等),则数据库存储的将是数值;如果声明为字符串类型(如varchar、text等),则存储的将是字符串。

类型声明只对读写属性生效,不涉及数据处理。例如:

为什么 $model->extra = ['a' => 1] 能转 JSON,但 $model->save(['extra' => ['a' => 1]]) 却存成 Array 字符串?

因为 ThinkPHP 的 JSON 自动编解码只在模型的属性访问器(__get/__set)中触发,而 save(['field' => $value]) 是绕过访问器、直写底层数据的。它把数组当普通值塞进 SQL 参数,PDO 默认把它转成 Array 字符串(如 "Array"),不是 JSON。

  • save(['extra' => ['a' => 1]]) → 数据库写入 "Array"(PHP 字符串强制转换结果)
  • $model->extra = ['a' => 1]; $model->save(); → 触发 setExtraAttr() → 自动 json_encode() → 存入 {"a":1}
  • 同理,$model->data(['extra' => ['a' => 1]])->save() 也绕过访问器,必须配合 allowField(true) + 前端传入已编码字符串,再手动 json_decode()

TP6 和 TP8 对 $json 属性的处理差异在哪?

TP6 要求你显式声明 protected $json = ['extra'];,否则字段永远是字符串;TP8 默认对数据库类型为 json 的字段启用自动 decode(目标为 array),但仅限于 find()/select() 查询结果——getData('extra')__get('extra') 仍可能返回原始字符串,取决于驱动是否注入 typecast。

  • TP6:不配 $json$model->extra 是字符串;配了 → 是数组(json_decode($value, true)
  • TP8:字段 DB 类型是 json → 查询结果中 $model->extra 默认是数组;但 $model->getData('extra') 返回未 decode 的原始字符串
  • 跨版本兼容写法:统一用 $model->extra 访问,避免混用 getData();写入一律走属性赋值,不用 save([...]) 直传数组

怎么安全地把用户提交的逗号分隔字符串转成 JSON 数组存库?

别碰 type => 'json'——那是给标准 JSON 字符串准备的。逗号分隔字符串属于非标格式,必须用自定义访问器,且要双向控制。

立即学习“PHP免费学习笔记(深入)”;

  • 数据库字段类型用 VARCHARTEXT,不要设成 JSON(MySQL 会拒绝非法值)
  • 模型里不写 $json,改用访问器:setTagsAttr() 接收字符串或数组,统一转成数组后 json_encode()getTagsAttr() 接收数据库原始字符串,json_decode()explode() 处理
  • 示例逻辑:

    public function setTagsAttr($value) { if (is_string($value) && !empty($value)) { $value = array_map('trim', explode(',', $value)); } return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } public function getTagsAttr($value) { $decoded = json_decode($value, true); return is_array($decoded) ? $decoded : ($value ? explode(',', $value) : []); }

最易被忽略的一点:JSON 字段内容修改后必须整体重新赋值才能持久化。$model->extra['name'] = 'new'; 不会触发变更检测,$model->save() 不会更新该字段——得写成 $data = $model->extra; $data['name'] = 'new'; $model->extra = $data; $model->save();