Yii2 ActiveRecord 默认推荐数据操作方式,需继承ActiveRecord并正确实现tableName()和primaryKey();find()返回ActiveQuery须调用one()/all(),save()自动区分增删,delete()仅支持实例删除。

Yii2 ActiveRecord 基础 CRUD:直接上能跑的代码
AR(ActiveRecord)在 Yii2 里不是“可选方案”,而是默认推荐的数据操作方式。只要模型类正确继承 yii\db\ActiveRecord,且数据库连接已配置,find()、save()、delete() 就能直接用——但前提是表名、主键、字段映射都对得上,否则会静默失败或抛出 InvalidConfigException 或 IntegrityConstraintViolationException。
定义模型类时必须重写 tableName() 和 primaryKey()
Yii2 不会自动推断表名,也不会默认把 id 当主键(哪怕它存在)。不显式声明,save() 可能插入后不返回 ID,update() 会全表更新,delete() 可能报错。
示例模型 User.php(放在 app/models/ 下):
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
public static function tableName()
{
return 'user'; // 必须小写,别写成 'User' 或 'users'
}
public static function primaryKey()
{
return ['id']; // 即使是单字段主键,也必须返回数组
}
}
常见坑:
- 表名写成
User(首字母大写)→ 查询时生成 SQL 表名为`User`,MySQL 报Table 'mydb.User' doesn't exist -
primaryKey()返回'id'(字符串)→update()失败,日志里出现Calling unknown method: yii\db\ActiveQuery::andWhere() - 表主键叫
user_id却没重写primaryKey()→findOne(123)查不到,因为默认按id匹配
查:find() 链式调用别漏掉 one() 或 all()
User::find() 返回的是 ActiveQuery 对象,不是数据。不加 one() 或 all(),后续直接 echo 或 foreach 会报 Fatal error: Cannot use object of type yii\db\ActiveQuery as array。
常用写法:
// 查单条(主键查询最简)
$user = User::findOne(123);
// 条件查询单条
$user = User::find()->where(['status' => 1, 'type' => 'admin'])->one();
// 查多条
$users = User::find()->where(['status' => 1])->orderBy('created_at DESC')->limit(10)->all();
// 查数量(不取数据,省资源)
$count = User::find()->where(['status' => 0])->count();
注意点:
-
findOne($id)底层等价于find()->where(['id' => $id])->one(),只适用于主键字段名是id或已重写primaryKey()的情况 - 用
andFilterWhere()替代where()处理可能为空的搜索条件,避免 SQL 注入和语法错误 -
asArray()能显著提升大批量读取性能:User::find()->asArray()->all()返回普通数组,不是对象
增删改:save() 是万能入口,但得先赋值
Yii2 的 save() 同时支持 insert 和 update,取决于模型是否已有主键值。不手动设主键或没调 findOne(),save() 就走插入;否则走更新。删用 delete(),但只能删已有实例(不能像 Laravel 那样 User::where(...)->delete())。
// 新增
$user = new User();
$user->name = 'Alice';
$user->email = 'a@example.com';
$user->status = 1;
if (!$user->save()) {
var_dump($user->errors); // 必须检查!save() 失败不抛异常,只返回 false
}
// 修改(两种等效写法)
$user = User::findOne(123);
$user->status = 0;
$user->save(); // 自动识别为 UPDATE
// 或批量赋值(需在模型里开启 scenarios / rules)
$user = User::findOne(123);
$user->load(['User' => ['status' => 0]], ''); // 第二个参数是 form name,CLI 场景传空串
$user->save();
// 删除
$user = User::findOne(123);
if ($user) {
$user->delete(); // 成功返回 int(1),失败返回 false
}
关键细节:
-
save()前不校验字段规则?那是因为模型没触发验证。默认save($runValidation = true),但如果模型规则里没定义name字段,$user->name = 'xxx'会被静默忽略——字段必须出现在rules()或safe列表中才允许批量赋值 - 想跳过验证强制保存?用
$user->save(false),但生产环境慎用 -
delete()是软删除还是硬删除,完全由你模型里的beforeDelete()行为决定,框架本身只执行 DELETE 语句
AR 看似简单,真正卡住人的永远是模型和数据库结构之间的映射细节——比如时间字段类型是 DATETIME 还是 TIMESTAMP,会影响 created_at 自动填充逻辑;再比如外键约束没关,delete() 直接报 Integrity constraint violation。这些不会在文档里标红加粗,但跑不通第一行 save() 就得回头核对。











