yii activerecord 应用于标准crud,复杂查询需慎用;模型须继承activerecord并实现tablename();查询优先用find()链式调用,避免n+1需合理使用with()预加载。

Yii 的 ActiveRecord 不是“用不用”的问题,而是“怎么用对、在哪别硬上”的问题。它适合标准 CRUD 场景,但直接拿它写复杂 JOIN 或聚合分析,容易生成低效 SQL 甚至报错。
怎么定义一个可用的 AR 模型
模型类必须继承 yii\db\ActiveRecord,且至少声明 tableName() 方法返回表名。Gii 自动生成的模型已包含该逻辑,手动写时别漏掉静态方法:
class User extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'user';
}
}
- 表名带前缀?用
\Yii::$app->db->tablePrefix配合字符串拼接,或在配置里统一设tablePrefix - 字段映射默认自动识别,无需额外声明;但若数据库字段名含大小写或特殊符号(如
user_name),PHP 属性仍用驼峰(userName),AR 会自动做下划线 ↔ 驼峰转换 - 不定义
tableName()会导致Unknown table: xxx错误,且不会提示具体缺哪一步
查询数据:one()、all()、find() 三者关系别搞混
findOne() 和 findAll() 是快捷静态方法,底层都调 find()->where(...)->one() 或 all()。真正灵活的是 find() 返回的 ActiveQuery 对象:
-
User::findOne(1)等价于User::find()->where(['id' => 1])->one(),但前者不能链式加with()或asArray() -
User::find()->where(['status' => 1])->orderBy('created_at DESC')->limit(10)->all()才是常规分页/排序写法 - 想查总数别用
count()套all(),直接User::find()->where(['status' => 1])->count()—— 后者只发 COUNT 查询,前者会把全部数据捞进内存再 count 数组长度 - 大批量读取时加
asArray(),比如User::find()->asArray()->all(),能省 30%+ 内存,尤其字段多、记录多时
增删改:save() 的验证开关和 updateAll/deleteAll 的边界
save() 默认开启验证(走 rules()),失败时不抛异常,只返回 false,需手动检查 getErrors():
$user = new User(); $user->name = 'test'; var_dump($user->save()); // false,但没报错- 跳过验证用
$user->save(false),适合后台脚本或已确保数据干净的场景 -
updateAll()和deleteAll()是静态方法,不触发验证、事件、关联逻辑,SQL 直出,快但“粗暴”——比如User::updateAll(['status' => 0], ['id' => 1])不会调用beforeSave事件 - 批量操作别用循环
findOne()->save(),N 条就 N 次查询;优先选updateAll()或事务包裹 + 批量插入
关联查询 with() 怎么避免 N+1,又不滥用
with() 是预加载核心,但它不是万能胶。写错位置或过度嵌套,反而让 SQL 更重:
-
User::find()->with('profile')->all()正确:先查 users,再用 IN 查询 profile -
User::find()->with(['profile' => function ($q) { $q->where(['active' => 1]); }])->all()可行,但注意子条件只作用于关联表,主表条件还得写在主 query 上 - 嵌套太深如
with(['profile', 'orders.items.product'])容易触发笛卡尔积,尤其 orders 和 items 都是多条时;此时应拆成两次查询,或改用joinWith()控制连接方式 -
with()不会自动加索引,如果关联字段(如user_id)没建索引,预加载反而比 N+1 更慢
最常被忽略的一点:AR 的懒加载(访问 $user->profile 自动查)看着方便,但在循环里用就是典型 N+1。哪怕只有一处 foreach ($users as $u) echo $u->profile->bio;,也得立刻补 with('profile')。











