Yii模型必须继承yii\db\ActiveRecord才能映射数据库表,否则save()、find()等方法不存在;需重写tableName()返回带前缀表名,属性由框架动态注入,字段映射需手动实现。

Yii 模型必须继承 yii\db\ActiveRecord 才能映射数据库表
不继承这个基类,save()、find()、one() 等方法根本不存在,也不会触发任何数据库操作。很多初学者写了个空类加 tableName() 就以为能用了,结果调用 save() 报 Fatal error: Call to undefined method。
正确做法是:
- 类必须
extends yii\db\ActiveRecord(Yii2)或CActiveRecord(Yii1) -
public static function tableName()返回带表前缀的表名,如'{{%user}}',不是'user' - 不需要手动定义属性(如
public $id),ActiveRecord 会根据表结构动态注入;但若需类型提示或 IDE 支持,可显式声明,注意类型要和数据库一致(如public int $status配合TINYINT(2))
字段映射不是框架原生功能,别指望 $_map 或 $_alias_ 自动生效
Yii 官方没有字段别名/表单字段到数据库字段的自动映射机制(不像 ThinkPHP 的 $_map)。所有所谓“映射”都是开发者自己写的辅助方法,比如 mymap() 或重写 afterConstruct(),它们不会改变 ActiveRecord 的底层行为。
常见错误现象:
- 写了
$_map = ['name' => 'username'],但没调用对应方法,$model->load($_POST)依然只填name字段,username保持 null - 在
afterConstruct()里用引用赋值($this->name = &$this->username),看似“绑定”,但load()只设置被提交的键,未提交字段不会被触发,且 PHP 8+ 中引用属性在序列化/验证时可能出问题 - 用
addslashes()在映射中手动转义,属于重复防御——Yii 的save()默认使用 PDO 预处理,SQL 注入风险已由框架覆盖
load() 和 attributes 赋值的区别直接影响字段是否入库
这是最易踩坑的操作点:直接赋值 $model->username = $_POST['name'] 不等于 $model->load($_POST),后者会走验证、安全过滤、事件钩子;前者绕过全部校验逻辑,连 rules() 里定义的 required 都不检查。
使用场景判断:
- 接收表单数据 → 用
$model->load($_POST),并确保rules()已定义安全规则(否则load()默认只接受safe属性) - 后台程序化填充(如 API 接口解析后赋值)→ 可用
$model->setAttributes($data, false),第二个参数false表示跳过安全校验,但需自行保证字段白名单 - 手动逐个赋值 → 仅用于极少数特殊字段(如时间戳、状态码),但务必确认该字段在
rules()中有对应验证,否则save()可能静默失败
数据库字段类型误判会导致值丢失,TINYINT(1) 是高频雷区
MySQL 的 TINYINT(1) 会被 Yii 的 Schema 自动识别为 boolean,读取时强制转成 true/false,原始值 0/1/2 中的 2 就变成 false,彻底丢失业务含义。
解决路径只有两条,没有中间选项:
- 建表时改用
TINYINT(2)或SMALLINT,Schema 就不会误标类型 - 无法改库时,在模型中重写
getTableSchema(),显式修正列类型:$schema->columns['status']->type = 'integer';,注意这会影响save()时的类型转换和验证行为,必须同步检查rules()是否允许整数范围
别依赖 afterFind() 里手动强转,那只是“表面修复”,后续 save() 还会按错误类型再转一遍,造成二次失真。


















