Laravel:getAttribute未附加到模型与访问器重复查询问题咨询
这个问题我之前也踩过坑,核心原因是默认的访问器是惰性执行的——每次你调用Auth::user()->articles_count时,都会重新执行访问器里的查询代码,完全不会自动缓存计算结果,所以调用3次就触发3次DB查询,确实挺坑的对吧?
问题根源
你写的访问器大概是类似这样的吧?
// User.php public function getArticlesCountAttribute() { return $this->articles()->count(); }
这种写法里,$this->articles()->count()每次被调用时,都会生成一条新的SELECT COUNT(*) FROM articles WHERE user_id = ?查询。Laravel不会帮你自动缓存这个值,因为访问器的设计逻辑就是“按需计算”,而非“初始化时预计算”。
解决办法(按推荐优先级排序)
1. 用Laravel原生的withCount预加载(最推荐)
这是Laravel官方为关联计数场景提供的最优方案,既能避免重复查询,性能表现也更好。
手动预加载:在获取用户的时候,提前把文章计数加载好
// 比如在控制器里获取当前用户时 $user = Auth::user()->withCount('articles')->first();之后在视图里不管调用多少次
$user->articles_count,都是用的预加载好的值,不会再触发数据库查询。全局自动预加载:如果项目里每个地方都需要这个计数,可以给User模型加全局作用域,自动带上这个统计:
// User.php protected static function booted() { static::addGlobalScope('articlesCount', function ($query) { $query->withCount('articles'); }); }这样每次获取User模型实例时,都会自动执行一次带计数的查询,之后所有调用都用缓存好的结果。
2. 在访问器内手动缓存结果
如果你不想用withCount,可以自己在访问器里把计算结果存在模型的attributes数组里,避免重复计算:
// User.php public function getArticlesCountAttribute() { // 先检查有没有已经计算好的值 if (!isset($this->attributes['articles_count'])) { // 计算后存在attributes里 $this->attributes['articles_count'] = $this->articles()->count(); } return $this->attributes['articles_count']; }
第一次调用时会执行查询并缓存,之后再调用就直接返回缓存的值,不会再触发DB查询。
3. 使用缓存系统(适合非实时场景)
如果文章数量不需要实时更新,可以用Laravel的缓存功能把结果存起来,比如缓存1小时:
// User.php use Illuminate\Support\Facades\Cache; public function getArticlesCountAttribute() { return Cache::remember("user_{$this->id}_articles_count", now()->addHour(), function () { return $this->articles()->count(); }); }
这样在缓存有效期内,不管调用多少次,都是用缓存里的值,不会查询数据库。
内容的提问来源于stack exchange,提问作者Badal




