Laravel 5.1的用户、角色和操作

4

我正在使用Laravel 5.1创建一个应用程序,其中涉及到“用户”、“角色”和“操作”。

表格设置如下:

用户

id    name
1     John Smith
2     Fred Smith

角色

id    name
1     Administrator
2     Customer

role_user

user_id    role_id
1          1
2          1

动作

id    name        description                    path
1     dashboard   ability to access dashboard    /admin
2     editAdmin   ability to edit admins         /admin/users/administrators/{id}/edit

action_role

action_id    role_id
1            1
2            1

user表包含站点上的所有用户,包括管理员和客户。

role表包含用户可能拥有的所有角色。例如,管理员客户

role_user表是将roleuser联系起来的中间表。

action表列出应用程序中所有可能的操作(即网址或路由)。

action_role是一个中间表,将actionrole相关联。

因此,总结如下:

  • 用户拥有角色
  • 角色拥有操作
  • 一个用户可以拥有多个角色
  • 一个角色可以拥有多个操作

我想设置一个中间件,在页面加载时检查用户是否有权限查看当前页面。为了做到这一点,我需要能够访问用户的操作,使用以下调用:

$user->roles()->actions();

如何设置我的 Eloquent 关系来支持这种调用?
更新:
我的模型中的关系设置如下:
User 模型:
/**
 * The roles that belong to the user.
 *
 * @return Object
 */
public function roles()
{
    return $this->belongsToMany('App\Models\User\Role')->withTimestamps();
}

角色

/**
 * The actions that belong to the role.
 *
 * @return Object
 */
public function actions() 
{
    return $this->belongsToMany('App\Models\User\Action');
}

/**
 * The users that belong to the role.
 *
 * @return Object
 */
public function users()
{
    return $this->belongsToMany('App\Models\User\User');
}

操作

/**
 * The roles that belong to the action.
 *
 * @return Object
 */
public function roles() 
{
    return $this->belongsToMany('App\Models\User\Role');
}

除了您已经提供的代码之外,您还想实现什么其他目标? - Gokigooooks
我的问题在帖子底部。我基本上只想检查用户是否有权限查看当前页面。使用上述表格设置实现这一点的最佳方法是什么? - V4n1ll4
Laravel 5 还是 Laravel 5.1? - Gokigooooks
5个回答

3

Eloquent没有跨越两个枢轴表的HasManyThrough关系。


1. 你可以通过惰性贪婪地加载角色和操作,然后提取它们来获取操作:

$actions = $user->load('roles.actions')->roles->pluck('actions')->collapse()->unique();

2. 您可以直接在数据库中检查操作,使用 whereHas 约束:

$allowed = $user->roles()->whereHas('actions', function ($query) use ($action) {
    $query->where('name', $action);
})->exists();

让我们在聊天中继续这个讨论 - V4n1ll4
顺便说一句,我刚发布了一个名为Bouncer的新包。它是Laravel的一个非常简单的角色和权限系统。你可能会喜欢它。即使你最终没有使用它,至少看一下这些查询,你也许会学到东西 :) - Joseph Silber

1

为了处理用户、用户角色和用户权限,您可以简单地使用Toddish包。

这个包为您做了很多事情,比如:

$user->is('Your Defined Role');
$user->can('add_user');
$user->level(7);

安装只需阅读其文档。

由于我正在构建一个相当大的应用程序,我更喜欢具有自主构建的灵活性。但还是谢谢你的建议。 - V4n1ll4
1
使用这个包,你不必担心灵活性的问题。这个包所做的就是为你创建数据库表和迁移,并提供一些有用的 PHP 类和函数。你可以根据自己的需求轻松开发或定制这个包。 - AliRNazari

0

嗨,这是我解决这个问题的方法。

我没有使用动作表。只有用户角色用户角色表。 解决方案后,我将我的自定义中间件传递给每个路由一个角色数组,例如:

Route::get('insights',[
    'as'=>'insights',
    **'middleware'=>['access.level'],**
    **'roles'=>['admin','customer'],**
    'uses'=>'CustomersController@index'
    ]);

修改的文件: app\Http\Controllers\Controller.php 自定义中间件:CheckAccessLevel app\Http\Kernel.php app\Http\routes.php

Controller.php 在这个文件中,我想要使用急切加载(eager load)当前登录用户及其角色,并将其作为全局变量在所有视图中使用,如{{currentUser}},并在所有控制器中使用"$this->currentUser"。这是代码:

    protected $currentUser;

public function __construct() {
    if(isset(Auth::user()->username)){

        $this->currentUser = User::with('roles')->where(['username' => Auth::user()->username])->first();

        // Share this property with all the views and controllers in your application.
        view()->share('currentUser', $this->currentUser);
    }
}

自定义中间件: CheckAccessLevel.php

在这个中间件中,我检索传递到路由的角色数组以及分配给当前用户的角色。 获取这些变量后,我对它们进行交集运算,以查看是否匹配,然后将用户传递下去。请查看以下代码:

//if user is not logged in, show them where to login :)
    if (!Auth::check()) {
        return redirect()->route('user::login');
    }


    //Otherwise get the roles for that route
    $get_route_action = $request->route()->getAction();
    $get_route_roles = $get_route_action['roles'];

    //Eager load the user with their list of roles
    $eager_user = User::with('roles')->where(['id'=>$request->user()->id])->first();
    $user_roles = $eager_user->roles->pluck('role_name')->toArray();

    //intersect the users roles with the route roles to see if there is a match
    foreach ($user_roles as $user_role){
        if(in_array($user_role, $get_route_roles)){
            return $next($request);
        }
    }

Kernel.php 在这里,我将我的路由注册为“access.level”并放置在路由中间件组内。

Route.php 然后,每当我创建一个路由时,我只需将允许的角色名称传递给它,就可以轻松实现了。

希望这能帮到你。


0

虽然您已经提到了您的Eloquent模型(以及它们之间的关系)已经设置好,但我假设您的应用程序中已经有以下内容:

用户模型

class User extends Eloquent {
    public function roles() {
        return $this->belongsToMany('App\Models\Role');
    }
}

榜样

class Role extends Eloquent {
    public function actions() {
        return $this->belongsToMany('App\Models\Action');
    }
    public function users() {
        return $this->belongsToMany('App\Models\User');
    }
}

动作模型

class Action extends Eloquent{
    public function roles(){ 
         return $this->belongsToMany('App\Models\Role'); 
    }
}

如果按照上述设置,你不能确定地进行以下调用,因为 Laravel 会认为你在调用查询构建器方法或其他不存在的方法:

$userActions = App\Models\User::find(1)->roles()->actions();

相反,您必须使用Laravel的神奇whereHas方法来查询涉及多个嵌套关系的关系。

现在,拥有用户的id和当前允许的action [实际上可以在您的中间件中利用],可以确定用户是否被允许查看页面:

$hasAccess = App\Models\User::whereHas('roles.actions', function($q) use ($id, $action){
    $q->where('users.id', $id);
    $q->where('actions.name', $action);
})->get()->toArray();
if($hasAccess){      // or `count($hasAccess) > 0`. The former will work since any number > 0 evaluates to true in PHP
    //user has access
}

请注意使用 . 进行关系的嵌套。

Laravel中的关系存在性:

在Laravel中,模型之间的关系存在性是通过haswhereHas方法来确定的。 has方法用于仅确定是否存在关系,例如通过执行App\User::has('roles')->get(),您将始终获得至少具有任何角色的用户列表/集合。 使用whereHas可以为实际查询添加where子句,从而增加更多功能。


我该如何选择 actions.name 的值,以便数组也包含它? - V4n1ll4

-3
你需要在这里使用HasManyThrough关系。以下是一些初始内容。
根据您提供的表,我看到它与用户和角色之间有一对多关系,而操作和角色也有多对多关系。
但首先,您需要为每个实体(用户、角色、操作)创建模型以遵循MVC结构。您可以使用Laravel的artisan命令行轻松创建这些模型,使用以下命令即可。
php artisan make:model User

确保在新创建的迁移中添加或更改列。假设您还为这3个表设置了枢轴表,现在可以添加关系。

class User extends Model {

//
protected $table = 'users';

/*
*   @var table
*
*/

public function action()
{

    return $this->hasManyThrough('App\Action', 'App\Role');
}

}

现在你拥有了一个HasManyThrough关系。你可以直接查询,不需要包含角色。

   //this
    $actions = User::find(1)->action();
    //instead of 
    $actions = User::find(1)->role()->action();
    //this will return all actions available to a user with id of 1

注意:我还建议您将您的角色和用户模型建立一对一的关系。


当我尝试上面的代码时,我收到Call to undefined method Illuminate\Database\Query\Builder::action()错误提示。role()函数似乎正常工作,但是当我使用action()函数时,它不喜欢它。 - V4n1ll4
我认为action()不能通过user模型进行调用。 - V4n1ll4
你能试着更改 action 函数的名称吗? - Gokigooooks
如果我改成 actions 我还是得到同样的结果。 - V4n1ll4
是的,当我删除action()调用时,role()运行良好。但是当我添加action()时,我会收到未定义方法错误。您确定role()->action()应该工作吗? - V4n1ll4
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接