Laravel中如何像缓存Auth::user()一样缓存角色数据查询?
问题
我开发的Laravel应用中,用户凭证存储在users表,不同角色对应独立的数据表(如admins、customers等)。Auth::user()在应用中多次调用仅执行一次数据库查询,但使用Admin::where('user_id', Auth::id())->first()或通过User模型关联调用Auth::user()->admin()时,每次调用都会执行查询;在模型访问器处理多行数据时,该查询甚至会重复执行数百次。请问如何像缓存Auth::user()一样缓存这类角色数据查询?
解决方案
1. 给User模型加自定义缓存属性
直接在User模型里封装角色数据的缓存逻辑,利用模型实例的内存存储,同一个请求内只会查询一次:
// app/Models/User.php public function admin() { return $this->hasOne(Admin::class); } public function customer() { return $this->hasOne(Customer::class); } // 自定义缓存的角色属性 public function getCachedRoleAttribute() { if (!isset($this->attributes['cached_role'])) { // 根据实际角色判断逻辑切换查询 if ($this->role === 'admin') { $this->attributes['cached_role'] = $this->admin; } elseif ($this->role === 'customer') { $this->attributes['cached_role'] = $this->customer; } } return $this->attributes['cached_role']; }
之后调用Auth::user()->cached_role即可,同一个用户实例只会触发一次关联查询。
2. 登录时预加载角色并绑定到Auth实例
在用户登录完成后,直接预加载对应角色的关联数据,后续调用无需再查库:
// app/Http/Controllers/Auth/LoginController.php protected function authenticated(Request $request, $user) { // 根据用户角色预加载对应关联 match($user->role) { 'admin' => $user->load('admin'), 'customer' => $user->load('customer'), }; // 把预加载的模型存到用户实例的自定义属性 $user->setAttribute('current_role', $user->{$user->role}); }
之后直接用Auth::user()->current_role就能拿到缓存的角色数据。
3. 请求级全局缓存
利用Laravel容器的请求周期缓存,封装成全局可用的辅助方法,整个请求内只查一次:
// 可以放在app/helpers.php里 function current_user_role() { return app()->remember('current_user_role', function () { $user = Auth::user(); if (!$user) return null; return match($user->role) { 'admin' => Admin::where('user_id', $user->id)->first(), 'customer' => Customer::where('user_id', $user->id)->first(), default => null, }; }); }
任何地方调用current_user_role(),同一个请求内只会执行一次数据库查询。
4. 优化循环内的访问器逻辑
如果是在模型访问器处理批量数据时重复查询,不要在访问器内直接调用角色查询,而是在控制器层先获取角色数据,再传递给集合处理:
public function index() { $currentRole = Auth::user()->cached_role; // 批量处理数据时直接复用已获取的角色数据 $items = Item::all()->map(function ($item) use ($currentRole) { $item->custom_field = $currentRole->permission; return $item; }); return view('items.index', compact('items')); }
内容的提问来源于stack exchange,提问作者Yousef Aburayyan
相关产品推荐
相关产品推荐

