ar新增数据时字段存不进数据库的根本原因是rules()中未声明为safe,导致load()静默丢弃前端传入的status、created_by等字段;必须显式在rules()中用['field', 'safe']或配合on场景声明,且字段名须与数据库列名严格一致。

AR 模式新增数据时,字段过滤靠 rules() + safe 属性控制,不是靠数据库默认值或模型属性赋值——不显式声明为 safe,哪怕前端传了,也会被静默丢弃。
为什么新增数据时某些字段没存进去
常见现象是:前端 POST 了 status、created_by、is_published,但数据库只写入了 title 和 content,其余字段为 NULL 或默认值。
- 根本原因:这些字段没在模型的
rules()中声明为'safe' -
ActiveRecord::load()(比如$model->load($request->post()))只会批量赋值safe列表里的字段 - 即使数据库字段允许 NULL、有默认值,AR 也不会自动补全;它只忠实地按规则过滤输入
怎么在 rules() 里正确声明可写字段
字段是否允许被批量赋值,和它是否“重要”“敏感”无关,只取决于业务是否允许前端直接提交。例如后台管理员可设 status,但普通用户不能——那就得拆开定义。
- 通用写法:
['status', 'is_published', 'category_id', 'created_by'], 'safe' - 按场景区分:用
on参数限定场景,如['status'], 'safe', 'on' => 'admin-create',然后调用$model->load($data, 'admin-create') - 禁止写入的字段(如
created_at、updated_at)不要加safe,改用beforeSave()自动填充 - 敏感字段(如
password_hash)不仅不该safe,还应在scenarios()中彻底移出对应场景
容易忽略的边界情况
看似简单,但几个细节一错就导致数据异常或安全漏洞:
- 字段名必须和数据库列名一致,不能写别名或驼峰(如
createdAt不行,得写created_at) -
load()默认使用''作为表单前缀,如果前端用了Article[status],就要$model->load($request->post(), ''),否则匹配失败 - 整型字段(如
status)若传了字符串"1",AR 不会自动转类型;需在rules()加'filter', 'filter' => 'intval'或用'integer'验证后处理 - 关联字段(如
user_id)如果来自当前登录用户,绝不能让前端传——应手动赋值:$model->user_id = \Yii::$app->user->id
真正决定哪些字段能进数据库的,不是你写了什么 SQL,也不是你有没有在表单里放 input,而是 rules() 里那几行 safe 声明。漏一条,就少一个可控入口;多一条,就多一个潜在风险点。











