如何在Laravel缓存中缓存具有类似角色关系的auth()->user()以减少对数据库的调用?



我正在构建一个使用灯塔php的应用程序。因为我经常为不同的用户设置各种策略,所以我经常在不同的部分应用程序中查询具有角色关系的用户模型,因此,我想将用户存储在Redis数据库中,然后从那里进行查询。我在网上读了几篇文章,比如:laravel缓存authuser;creating-a-caching-user-provider-for-laravel/;caching-the-laravel-user-provider-with-a-decorator/,在这里查看了laravel-auth-user中的代码,有点理解这个概念,但很难深入理解laravel以找到合适的解决方案。。。

例如,我很难理解如何在UserObserver的事件方法中存储具有Role关系的User,很清楚如何使用一个Model而不使用附加的关系。

我有一种感觉,我应该做这样的事情:

class UserObserver
{
/**
* @param User $user
*/
public function saved(User $user)
{
$user->load('role');
Cache::put("user.$user->id", $user, 60);
}
}

但通过这种方式,我对DB进行了两次调用,而不是预先加载关系。我如何在events参数中预加载关系。我尝试添加protected $with = ['role'],以便始终加载子模型/关系。但无论怎样,我都会对DB进行更多的调用,以检索Role或检索User和Role。

他是我的灯塔php项目中的一些简化代码示例。

schema.graphql:

type SomeType {
someMethod(args: [String!]): [Model!] @method @can(ability: "isAdmin",  model: "App\Models\User")
}
type User {
id: ID
name: String
role: Role @belongsTo
}
type Role {
id: ID!
name: String!
label: String!
users: [User!] @hasMany
}

具有角色关系的用户模型:

class User extends Authenticatabl {
public function role(): BelongsTo
{
return $this->belongsTo(Role::class);
}
}

在一些graphql类型字段上使用的用户策略:

class UserPolicy
{
use HandlesAuthorization;
public function isAdmin(): Response
{
$user = auth()->user();
return $user->role->name === 'admin'
? $this->allow()
: $this->deny('permission denied');
}
public function isManager(): Response
{
$user = auth()->user();
$this->allow();
return $user->role->name === 'manager' || $user->role->name === 'admin'
? $this->allow()
: $this->deny('Permission Denied');
}
}

Lighouse自定义查询类,用于通过方法解析字段。

class SomeType {
public function someMethod(): string
{
// this triggers db call rather than receiving `role->name` from redis along with user
return auth()->user()->role->name;
}
}

如果我创建的graphql查询看起来像这样(请参阅下面(,它会导致从数据库而不是缓存加载角色关系。

query {
user {
id
name
role {
id
name
}
}
}
Please help.

您可以通过为User模型上的role属性创建自定义accessor来缓存关系。例如:

<?php 
use IlluminateSupportFacadesCache; 
class User extends Authenticatabl {
public function role(): BelongsTo
{
return $this->belongsTo(Role::class);
}
public static function getRoleCacheKey(User $user): string
{
return sprintf('user-%d-role', $user->id);
}
// Define accessor for caching purposes
public function getRoleAttribute(): Collection
{
if ($this->relationLoaded('role')) {
return $this->getRelationValue('role');
}

// Replace 3600 for the amount of seconds you would like to cache
$role = Cache::remember(User::getRoleCacheKey($this), 3600, function () {
return $this->getRelationValue('role');
});
$this->setRelation('role', $role);
return $role;
}
}

或者,您可以使用rememberForever()函数永久缓存它。请注意,您必须编写一个实现来手动删除/更新缓存,因为它将永远保持该值。您可以创建一个清除缓存的函数,如下所示:

// In User model
public static function forgetRoleCaching(User $user): bool
{
return Cache::forget(sprintf(User::getRoleCacheKey($user));
}

您在观察者中的代码可以更新为以下内容:

class UserObserver
{
/**
* @param User $user
*/
public function saved(User $user)
{
// in case user role is cached forever
User::forgetRoleCaching($user);
$user->load('role'); // will trigger the accessor an should cache again
Cache::put("user.$user->id", $user, 60);
}
}

您可以使用Session存储您的身份验证用户和您的关系。

示例:

$auth = Auth::User();
Session::put('user',$auth);
Session::put('relation',$auth->relation);

希望它能帮助你

创建一个名为"user.php";在";App\Config"路径文件应该像这个

<?php
return [
'role' => ''
];

现在你可以设置一个配置

config(['user.role' => $example_role]);

然后你可以从任何文件中读取,只要你需要这个数据

config('user.role');

这是我的解决方案,我在中间件中设置了这个配置值,工作完美。

最新更新