Laravel 编码教程:自己写一个用户角色系统
TL;DR(Too Long; Didn't Read的缩写)
注意: 通过 Role::wrap
方法,您可以在数据库中“随时随地”的创建角色。因此,无需为您的数据库添加角色即可开始使用它们。如果您仍然希望使用角色为数据库设置种子数据,请使用方法Role::fromArray(['admin', 'vip', 'guest'])
。
// 在路由上使用:
Route::get('admin', 'AdminController@index')->middleware('role:admin');
// 为用户增加一个角色:
$user->addRole('admin');
// 移除用户某个角色:
$user->removeRole('admin');
// 选择具有`admin`角色的用户:
User::whereRole('admin')->get();
// 获取用户的角色:
$user->roles; // Collection
$user->roles(); // Query Builder
判断用户是否具有角色:
// 如果用户具有“admin”角色,则为true;否则为false。
$user->hasRole('admin');
//如果用户具有“guest”或“vip”(或两者兼有)角色,则为true,否则为false。
$user->hasAnyRole('guest', 'vip');
// 如果用户同时具有“admin”和“accounting”角色,则为true;否则为false。
$user->hasRoles('admin', 'accounting');
实现
1. 迁移
运行: php artisan make:migration create_roles_table --create=roles
使用以下内容填充:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRolesTable extends Migration
{
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('roles');
}
}
运行: php artisan make:migration create_role_user_table --create=role_user
使用以下内容填充:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRoleUserTable extends Migration
{
public function up()
{
Schema::create('role_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->foreignId('user_id');
$table->foreignId('role_id');
$table->timestamps();
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade')->onUpdate('cascade');
$table->foreign('role_id')
->references('id')->on('roles')
->onDelete('cascade')->onUpdate('cascade');
});
}
public function down()
{
Schema::dropIfExists('role_user');
}
}
2.模型 与 Traits
运行: php artisan make:model Role
使用以下内容填充:
namespace App;
use App\HasName;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
use HasName;
protected $fillable = [
'name',
'description',
];
public function users()
{
return $this->belongsToMany(User::class);
}
}
创建文件 app/Concerns/HasName.php
使用以下内容填充:
namespace App;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Collection;
trait HasName
{
public static function findFromName(string $name): self
{
return self::whereName($name)->firstOrFail();
}
public static function findOrCreate(string $name, string $description = ""): self
{
try {
return self::findFromName($name);
} catch (ModelNotFoundException $e) {
return self::create(compact('name'));
}
}
public static function wrap($name): self
{
if (is_string($name)) {
$name = self::findOrCreate($name);
}
if (is_array($name)) {
$name = self::firstOrCreate($name);
}
if (! $name instanceof self) {
throw new \InvalidArgumentException("\$name should be string, array, or " . self::class);
}
return $name;
}
public static function fromArray(array $array): Collection
{
return (new Collection($array))->map(fn($item) => self::wrap($item));
}
}
创建文件 app/Concerns/HasRoles.php
文件代码:
namespace App;
use App\Role;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
trait HasRoles
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function hasRole($role): bool
{
return $this->roles()->get()->contains(Role::wrap($role));
}
public function hasRoles(...$roles): bool
{
foreach (Arr::flatten($roles) as $role) {
if (! $this->hasRole($role)) {
return false;
}
}
return true;
}
public function hasAnyRole(...$roles): bool
{
foreach (Arr::flatten($roles) as $role) {
if ($this->hasRole($role)) {
return true;
}
}
return false;
}
public function addRole($role): void
{
$this->roles()->attach(Role::wrap($role));
}
public function removeRole($role): void
{
$this->roles()->detach(Role::wrap($role));
}
public function scopeWhereRole(Builder $query, $role): Builder
{
return $query->whereHas('roles', function($query) use ($role) {
return $query->where(DB::raw('"roles"."id"'), Role::wrap($role)->id)
});
}
}
将此添加到 app/User.php
:
namespace App;
use App\Concerns;
class User
{
use Concerns\HasRoles;
}
3. 中间件
Create a new file app/Http/Middleware/Role.php
文件代码如下:
namespace App\Http\Middleware;
use Illuminate\Auth\Access\AuthorizationException;
class Role
{
public function handle($request, \Closure $next)
{
$roles = array_slice(func_get_args(), 2);
if (! $request->user()->hasAnyRole($roles)) {
throw new AuthorizationException("You don't have the required role to access this resource.");
}
return $next($request);
}
}
更新 app/Http/Kernel.php
:
protected $routeMiddleware = [
'role' => \App\Http\Middleware\Role::class,
];
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
namespace App;
use App\Concerns;
class User { use Concerns\HasRoles; }
这段代码没有看懂,到底什么意思!