如何使用ThinkPHP模型修改器实现数组自动转换为JSON并存储?
- 内容介绍
- 文章标签
- 相关推荐
本文共计932个文字,预计阅读时间需要4分钟。
在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免费学习笔记(深入)”;
- 数据库字段类型用
VARCHAR或TEXT,不要设成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模型中,直接给字段赋数值时,数据库中存储的仍然是原始的数据类型,而非字符串。如果模型中的字段类型声明为数值类型(如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免费学习笔记(深入)”;
- 数据库字段类型用
VARCHAR或TEXT,不要设成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();。

