优先用activerecord处理单条记录的增删改,因其自动触发验证、事件和软删除;批量操作、复杂sql或高性能插入时选dao。

要在 Yii2 项目中决定用 DAO 还是 ActiveRecord 处理数据库操作,必须明确当前动作是否涉及业务逻辑、是否需要验证或事件响应、是否要复用模型层契约——绕开 AR 直接写 SQL 可能导致软删除失效、缓存不一致、类型转换丢失。
查单条记录且后续要 save() 或 delete()
必须用 ActiveRecord。
第一步:调用 User::findOne($id) 获取模型实例。
第二步:直接修改属性,例如 $user->status = 1。
第三步:执行 $user->save() —— 这会自动触发 beforeSave 事件、运行验证规则、处理时间戳行为,并保持 deleted_at IS NULL 软删除条件。
【漏掉软删除条件就可能更新/删除已被逻辑删除的记录】
若改用 DAO:Yii::$app->db->createCommand("UPDATE user SET status=1 WHERE id=:id", [':id' => $id])->execute(),则完全跳过所有 AR 层契约,【不会检查 deleted_at,也不会触发任何事件或验证】。
批量更新字段且不触发 beforeSave
这时 DAO 更合适。
方法一:用 createCommand()->update()
Yii::$app->db->createCommand()->update('user', ['status' => 2], ['id' => $ids])->execute();
这一步操作起来很简单,直接传表名、字段数组、WHERE 条件即可,不加载模型、不走 AR 生命周期。
方法二:用原生 SQL 批量更新(支持更复杂逻辑)
Yii::$app->db->createCommand("UPDATE user SET status = CASE WHEN created_at strtotime('-30 days'), ':ids' => $ids])->execute();
注意:参数必须绑定,不能拼接字符串,否则有 SQL 注入风险。
需要关联查询且返回结构化对象
优先用 ActiveRecord 的 with() 和关系定义。
在 User 模型中声明:public function getPosts() { return $this->hasMany(Post::class, ['user_id' => 'id']); }
然后一行代码获取用户及其全部文章:$user = User::find()->with('posts')->where(['id' => $id])->one();
DAO 无法自动解析这种嵌套结构;若硬用 findBySql,得手写 JOIN 并手动组装数组,还要自己处理一对多去重和嵌套,极易出错。
这一步不能省略模型层的关系声明——没有 getPosts() 方法,with('posts') 就会报错。
插入大量数据提升性能
DAO 的 batchInsert 比循环调用 AR 的 save() 快 5 倍以上。
准备二维数组,每行对应一条记录:
$rows = [ ['username' => 'a', 'email' => 'a@b.com', 'created_at' => time()], ['username' => 'b', 'email' => 'b@b.com', 'created_at' => time()] ];
执行批量插入:
Yii::$app->db->createCommand()->batchInsert(User::tableName(), ['username', 'email', 'created_at'], $rows)->execute();
AR 的 insertAll() 方法底层也是调用 DAO 的 batchInsert,但封装后丢失了对字段类型的显式控制权,某些场景下反而不如直接用 DAO 稳定。











