【问题标题】:Laravel 5.1 users, roles and actionsLaravel 5.1 用户、角色和操作
【发布时间】:2015-12-09 16:18:04
【问题描述】:

我正在使用 Laravel 5.1 和 usersrolesactions 创建一个应用程序。

表格设置如下:

用户

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 表包含用户可以拥有的所有可能角色。例如 AdministratorCustomer

role_user 表是一个将role 链接到user 的数据透视表。

action 表列出了应用程序上所有可能的操作(即 URL 或路由)。

action_role 是一个将action 链接到role 的数据透视表。

总结一下:

  • 用户具有角色
  • 角色有动作
  • 一个用户可以有多个角色
  • 一个角色可以有很多动作

我想要一个中间件设置,如果用户有权查看当前页面,它会检查页面加载。为此,我需要能够使用如下调用访问用户操作:

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

我如何建立我的雄辩关系来支持这种呼叫?

更新

我的关系在我的模型中是这样设置的:

用户

/**
 * 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');
}

【问题讨论】:

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

标签: php laravel laravel-5


【解决方案1】:

你需要一个 HasManyThrough 关系。这里有一些东西可以帮助你开始。

从您给定的表格来看,我发现它与用户和角色具有一对多的关系。而动作和角色也有多对多的关系。

但首先您需要为每个实体(用户、角色、操作)创建模型以遵循 MVC 结构。您可以使用 Laravel 的工匠使用此命令轻松地从命令行制作这些模型。

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() 时它不喜欢它。
  • 我认为action() 不能通过user 模型调用。
  • 可以试试改action的函数名吗?
  • 如果我改变行动,我仍然会得到同样的结果。
  • 您有用于操作和角色的数据透视表吗?
【解决方案2】:

虽然您提到您的 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 会假定您正在调用 Query Builder 方法或任何[实际上不存在的]方法:

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

相反,您必须使用 Laravel 的 magical 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 值以便数组也包含该值?
【解决方案3】:

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

这个包为你做了很多事情。喜欢:

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

对于安装,只需阅读它的文档。

【讨论】:

  • 由于我正在构建一个相当大的应用程序,因此我更愿意灵活地构建自己的应用程序。不过谢谢你的建议。
  • 这个包的灵活性没有什么好担心的。这个包所做的只是为您创建数据库表和迁移。很少有一些带有一些方便功能的php类。您可以根据需要轻松开发或自定义此软件包。
【解决方案4】:

Eloquent 在 2 个数据透视表中没有 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();

【讨论】:

  • 顺便说一句,我刚刚发布了一个名为Bouncer 的新包。对于 Laravel 来说,这是一个非常简单的角色和能力系统。你可能会喜欢。即使你最终没有使用它,至少看看the queries。你可能会学到一些东西:)
【解决方案5】:

您好,我就是这样解决这个问题的。

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

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

文件修改: 应用程序\Http\Controllers\Controller.php 自定义中间件: CheckAccessLevel 应用\Http\Kernel.php 应用\Http\routes.php

Controller.php 在这个文件中,我想用他的角色急切地加载当前登录的用户,并将其作为 {{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);
        }
    }

内核.php 在这里,我在路由中间件组中将我的路由注册为“access.level”

Route.php 然后每当我创建一条路线时,我只需将允许的角色名称传递给它,瞧!它对我有用..

我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2016-07-23
    • 2022-01-01
    • 2016-09-13
    • 2016-04-07
    • 2016-08-24
    • 2016-04-07
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多