英文:
Error eager loading roles with users laravel permissions
问题
When attempting to eager load roles with their assigned users from Spatie's laravel-permissions library like this:
use Spatie\Permission\Models\Role;
Role::with('users')->get();
This error occurs:
Error: Class name must be a valid object or a string in file vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php on line 791
The code above works just fine in Laravel's PsySH powered Repl Tinker, as mentioned in this StackOverflow post: StackOverflow post.
So I thought if this only happens in HTTP requests, it must be due to a middleware issue.
I am using Laravel Sanctum for API authentication, and thus the route is under the middleware auth:sanctum
in routes/api.php
:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/roles', [RolesController::class, 'index']);
}
It also works if I move the route out of the middleware, but I shouldn't, since only authenticated users should be able to access that endpoint.
I'm guessing this has something to do with the fact that roles get created with the guard_name
as web
in the database by default, but I'm not sure how to fix it.
英文:
When attempting to eager load roles with their assigned users from Spatie's laravel-permissions library like this
use Spatie\Permission\Models\Role;
Role::with('users')->get();
This error occurs
> Error: Class name must be a valid object or a string in file vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php on line 791
The code above works just fine in the Laravel's PsySH powered Repl Tinker, see this StackOverflow post
So I thought if this only happens in HTTP requests, it must be due to a middleware issue
Am using Laravel Sanctum for API authentication and thus the route is under the middleware auth:sanctum
in routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/roles', [RolesController::class, 'index']);
});
It also works if I move the route out of the middleware but I shouldn't, since only authenticated users should be able to access that endpoint
Am guessing this has something to do with the fact that roles get created with the guard_name
as web
in the database by default, but am not sure how to fix it
答案1
得分: 0
简要概述
在 config/auth.php
文件中,将以下内容添加到 guards
数组中:
'sanctum' => [
'driver' => 'sanctum',
'provider' => 'users'
],
解释
问题在于 Sanctum 的服务提供程序为提供者设置了一个值为 null
的身份验证守卫,请参阅源代码
class SanctumServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
config([
'auth.guards.sanctum' => array_merge([
'driver' => 'sanctum',
'provider' => null, // <=== 这是问题所在
], config('auth.guards.sanctum', [])),
]);
if (! app()->configurationIsCached()) {
$this->mergeConfigFrom(__DIR__.'/../config/sanctum.php', 'sanctum');
}
}
这导致 Laravel-Permission 的 getModelForGuard()
辅助函数 在这里
function getModelForGuard(string $guard)
{
return collect(config('auth.guards'))
->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
->get($guard);
}
在需要对 Sanctum 守卫进行身份验证的中间件时,返回空值,因此预加载失败 在这里
/**
* A role belongs to some users of the model associated with its guard.
*/
public function users(): BelongsToMany
{
return $this->morphedByMany(
// Error occurs here
getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
'model',
config('permission.table_names.model_has_roles'),
app(PermissionRegistrar::class)->pivotRole,
config('permission.column_names.model_morph_key')
);
}
在 auth/config.php
中手动插入具有适当提供者的守卫后,预加载将再次正常工作
'guards' => [
...
'sanctum' => [
'driver' => 'sanctum',
'provider' => 'users'
]
],
英文:
TL;DR?
in config/auth.php
add this to the guards
array
'sanctum' => [
'driver' => 'sanctum',
'provider' => 'users'
],
Explanation
The problem is that Sanctum's service provider sets an authentication guard with a null
value for the provider, see source code here
class SanctumServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
config([
'auth.guards.sanctum' => array_merge([
'driver' => 'sanctum',
'provider' => null, // <=== This is the issue
], config('auth.guards.sanctum', [])),
]);
if (! app()->configurationIsCached()) {
$this->mergeConfigFrom(__DIR__.'/../config/sanctum.php', 'sanctum');
}
}
This causes Laravel-Permission's getModelForGuard()
helper function here
function getModelForGuard(string $guard)
{
return collect(config('auth.guards'))
->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
->get($guard);
}
to return nothing when middleware requires the Sanctum guard to be authenticated, hence the eager load fails here
/**
* A role belongs to some users of the model associated with its guard.
*/
public function users(): BelongsToMany
{
return $this->morphedByMany(
// Error occurs here
getModelForGuard($this->attributes['guard_name'] ?? config('auth.defaults.guard')),
'model',
config('permission.table_names.model_has_roles'),
app(PermissionRegistrar::class)->pivotRole,
config('permission.column_names.model_morph_key')
);
}
After manually inserting the guard in auth/config.php
with a proper provider, eager loading works again
'guards' => [
...
'sanctum' => [
'driver' => 'sanctum',
'provider' => 'users'
]
],
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论