Некоторые вещи в Laravel, на мой взгляд, сделаны довольно странно. Одна из них – это то, как предлагается реализовывать роли пользователей инструментами фреймворка. В гайде от разработчиков предлагается проверять роль пользователя в middleware. Это значит, что на момент авторизации приложение ничего не знает про роли и доступы. За авторизацию отвечает guard, и в конце статье не рекомендуется их использовать для реализации ролей.
Но в сети есть немало примеров, предлагающих сделать разделение ролей с помощью guard.
- https://pusher.com/tutorials/multiple-authentication-guards-laravel/
- https://webjourney.dev/how-to-implements-multiple-authentication-for-a-website-using-laravel-10-with-guards
- https://github.com/mberecall/Laravel-8-Multi-Guards-Authentication/
Правда все эти примеры имеют фатальный недостаток: пользователи с разными ролями должны находится в разных таблицах. Довольно странно создавать 5 одинаковых таблиц для 5 ролей.
Поэтому я предлагаю другое решение, благо это не сложно сделать, по крайней мере в последних версиях.
Кастомный guard user provider
Функциональность guard делится на driver
и provider
. Последний нам и нужно кастомизировать.
Для начала добавим поле role в таблицу users, где и будут хранится роли.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role', 100)->after('remember_token')->default('user');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
};
Добавляем admin guard. Меняем driver user provider на свой: я назвал его eloquentWithRole
. Поле role в конфиге – это значение, которое будет храниться в базе данных.
<?php
return [
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquentWithRole',
'model' => App\Models\User::class,
'role' => 'user'
],
'admins' => [
'driver' => 'eloquentWithRole',
'model' => App\Models\User::class,
'role' => 'admin'
],
],
]
И наконец, создаем свой провайдер на основе базового: добавляем поиск по полю role в запрос, который должен возвращать пользователя.
<?php
namespace App\Providers;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Auth::provider('eloquentWithRole', function (Application $app, array $config) {
$userProvider = new EloquentUserProvider($app['hash'], $config['model']);
$userProvider->withQuery(function ($query) use ($config) {
$query->where('role', $config['role']);
});
return $userProvider;
});
}
}
Теперь можно использовать guard admin. Пример для авторизации
Auth::guard('admin')->attempt($credentials)