yii activerecord默认不触发懒加载,关联属性如$model->orders为null,须显式调用with()预加载;laravel eloquent则默认自动懒加载,易引发n+1问题。

yii\db\ActiveRecord 默认不触发懒加载
Yii 的 ActiveRecord 实例化后,$model->orders 这种关联属性默认是 null,必须显式调用 with() 或 joinWith() 才会查数据库;Laravel 的 Eloquent 在访问未加载的关联时(如 $user->posts),会自动触发懒加载(lazy loading),哪怕你只读一次,也多一次 SQL 查询。
常见错误现象:foreach ($users as $u) { echo $u->profile->name; } 在 Yii 中直接报错(Trying to get property 'name' of null),反而帮你提前暴露 N+1;在 Laravel 中静默执行 100 次额外查询,压测时 DB CPU 突增却找不到源头。
- Yii 要预加载三级关联,必须写
User::find()->with(['profile', 'posts.comments'])->all() - Laravel 默认行为是“按需加载”,
with()是显式优化手段,不是默认路径 - 开启
'enableLazyLoading' => false可禁用 Laravel 懒加载,但会破坏大量现有代码逻辑
schemaCache 开启后跳过每次元数据查询
Yii 的 ActiveRecord 在首次查询某张表前,会执行 SHOW FULL COLUMNS FROM `user` 获取字段类型、主键、索引等信息——这个动作默认每请求都做,高并发下变成隐性 SQL 压力源。Laravel 的 Eloquent 不查 schema,靠 migration 文件或手动定义 $casts/$fillable,但代价是字段变更后容易类型失配、JSON 字段解析失败。
实操建议:在 config/db.php 中必须同时配置三项:
'enableSchemaCache' => true-
'schemaCacheDuration' => 3600(单位秒,设为 0 不生效) -
'schemaCache' => 'cache'(确保cache组件已正确定义且可用)
漏掉任意一项,Yii 都会静默降级为无缓存模式,且不报错。上线后表结构变更,需手动执行 Yii::$app->cache->flush(),否则新字段读不到。
AR 查询不自动触发模型事件和 boot() 钩子
Yii 的 find()->one() 或 find()->all() 是纯数据获取,不走 init()、afterFind() 等生命周期钩子;而 Laravel 的 User::find(1) 会完整触发 boot()、bootIfNotBooted()、newFromBuilder()、fireModelEvent('retrieved') 等至少 5 层调用链,中间还含反射和事件分发。
性能影响明显:实测单次查询,Yii AR 平均耗时 1.2ms(含 PDO 执行),Laravel Eloquent 同场景平均 2.7ms(PHP 层开销占 60%)。当批量查 1000 条记录时,Yii 多半保持线性增长;Laravel 容易因事件监听器注册过多,出现非线性延迟跳变。
- Yii 若需类似行为,得显式在
afterFind()里写逻辑,不会默认加载 - Laravel 的
withoutEvents()可绕过,但无法全局关闭,且对findMany()无效 - 第三方包(如
spatie/laravel-query-builder)叠加后,Eloquent 的钩子层数可能翻倍
toArray() 和 asArray() 的内存与序列化差异
Yii 的 asArray() 返回原生 PHP 数组,零对象实例化开销;toArray() 才创建完整 AR 对象。Laravel 的 toArray() 是唯一出口,所有 getAttributes()、jsonSerialize()、API 响应最终都走它,意味着每个模型必经对象构造 + 属性遍历 + 类型转换三步。
大数据导出场景下尤其关键:导出 10 万行订单,Yii 用 Order::find()->asArray()->each(function ($row) { echo json_encode($row); }) 内存恒定在 ~8MB;Laravel 用 Order::cursor()->each(...) 仍需实例化 10 万个 Order 对象,内存峰值常破 200MB,GC 压力大。
容易被忽略的一点:asArray() 下的关联数据仍是数组嵌套,不会自动转成对象,$row['customer']['name'] 可直接用,但 $row['customer']->name 会报错——这反而是防止误用、减少隐性开销的设计选择。











