权限
权限是一个重要的模块,很多新手写好这个模块,从现在开始,我们做做权限,基于RBAC0的权限模型,如果对这个不了解,请转到RBAC,这篇文章基本把原理介绍清楚了。
RBAC一共使用了六张表,分别是
- 用户表(admins)
- 角色表(roles)
- 权限表(permissions)
- 用户-角色关系表(admin_roles)
- 角色-权限关系表(role_permissions)
- 菜单表(menus)
用户-角色关系表是多对多关系,因为一个用户可以拥有多个角色,比如我是技术总监,同时呢我还是产品经理,那我就有两个角色了,同样的,一个角色也可以被多个人拥有,比如产品经理角色,有些公司产品经理是很多的。
角色-权限关系表也是对多多关系,一个角色可以拥有多个权限,一个权限也可以对应多个角色。
用户-角色关系、角色-权限关系是多对多关系相对来说更加灵活,从程序设计角度看,要考虑到业务的变更,同时这个部分改起来比较麻烦,所以我选择多对多关系。
菜单和权限是一对多关系,
使用数据迁移生成表,我们命令行走起
php artisan make:migration create_admins_table
php artisan make:migration create_roles_table
php artisan make:migration create_permissions_table
php artisan make:migration create_admin_roles_table
php artisan make:migration create_role_permissions_table
五个命令,一个行一行的执行,我们从admins表开始
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('user_name',20)->comment('用户名');
$table->string('password',100)->comment('密码');
$table->string('email',30)->default('')->comment('电子邮箱');
$table->boolean('state')->default(0)->comment('状态,0:未启用,1:已启用');
$table->timestamps();
});
}
角色表
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('role_name',20)->comment('角色名');
$table->boolean('state')->default(0)->comment('状态,0:未启用,1:已启用');
$table->timestamps();
});
}
权限表
public function up()
{
Schema::create('permissions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedInteger('menu_id')->comment('菜单ID');
$table->string('name',30)->comment('权限名称');
$table->string('path',50)->comment('路由文件中定义的路径');
$table->timestamps();
});
}
用户角色关系表
public function up()
{
Schema::create('admin_roles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedInteger('admin_id')->comment('管理员ID');
$table->unsignedInteger('role_id')->comment('角色ID');
$table->timestamps();
});
}
角色权限关系表
public function up()
{
Schema::create('role_permissions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedInteger('role_id')->comment('角色ID');
$table->unsignedInteger('permission_id')->comment('权限ID');
$table->timestamps();
});
}
菜单表
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedInteger('parent_id')->default(0)->comment('父ID');
$table->string('name',30)->comment('名称');
$table->timestamps();
});
}
执行以下命令,生成以上表
php artisan migrate
六张表很快就生成了
然后我们把模型给建立起来。切换到/app/Models/目录,同样的六个模型文件,蓝色的文件名是新建的。
我们先在模型里写个列表方法list()。菜单最多三级,所以,我with了三个child。菜单是树形结构,是一对多关系,所以使用hasMany。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
public function child()
{
return $this->hasMany(Menu::class, 'parent_id', 'id');
}
public static function lists()
{
return Menu::with('child.child.child')->where('parent_id',0)->get();
}
}
我们定以下顺序吧,顺序是菜单管理、权限管理、角色管理、用户管理。
分别在/app/Http/Controllers/Admin/目下建立MenuController.php文件和/resources/views/admin/目录下新建menu目录,并且新建list.blade.php文件。对list.blade.php文件来说,复制文章分类的列表就行,然后改改就可以了。
控制器文件
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Menu;
use Illuminate\Http\Request;
class MenuController extends Controller
{
public function lists(Request $request)
{
$data['menu'] = Menu::lists();
return view('admin.menu.list', $data);
}
}
视图列表文件
再新写一组菜单的路由
// 菜单
$router->get('/menu/list', 'MenuController@lists'); // 菜单列表
$router->addRoute(['GET', 'POST'],'/menu/add', 'MenuController@add'); // 添加菜单
$router->addRoute(['GET', 'POST'],'/menu/addChild/{id:[1-9]+}', 'MenuController@addChild'); // 添加子菜单
$router->addRoute(['GET', 'POST'],'/menu/edit/{id:[1-9]+}', 'MenuController@edit'); // 编辑菜单
$router->post('/menu/del', 'MenuController@del'); // 删除菜单
刷新页面看看效果
添加菜单
在菜单模型中添加一个add()方法。
public static function add(array $data)
{
$menu = new Menu();
!empty($data['parent_id']) && $menu->parent_id = (int)$data['parent_id'];
$menu->name = $data['name'];
return $menu->save();
}
在菜单控制器中,添加add()方法
public function add(Request $request)
{
if ($request->post()) {
// todo 对新增数据做验证
Menu::add($request->input());
return $this->response->responseJSON();
}
return view('admin.menu.add');
}
在菜单视图目录下新增一个add.blade.php文件,复制其它模块的add.blade.php文件就行
菜单编辑
在菜单模型中,新增一个edit()方法
public static function edit(Menu $menu, $data)
{
!empty($data['parent_id']) && $menu->parent_id = (int)$data['parent_id'];
$menu->name = $data['name'];
return $menu->update();
}
控制中新增一个编辑方法
public function edit(int $id, Request $request)
{
$category = Menu::where('id', $id)->first();
// if(empty($category->id)){
// //跳转到错误页面
// }
if ($request->post()) {
// todo 数据验证
Menu::edit($category, $request->input());
return $this->response->responseJSON();
}
$data['menu'] = $category;
return view('admin.menu.edit', $data);
}
在菜单视图目录下,新建一个eidt.blade.php文件。然后我们编辑看看
编辑好了,接下来做添加子菜单
模型中已经有了添加菜单的方法了,所以我们就不用再改模型了,在控制器中新增一个addChild方法
public function addChild(int $id, Request $request)
{
// 同时获取父级的信息
$category = Menu::with('parent')->where('id', $id)->first();
// if(empty($category->id)){
// //跳转到错误页面
// }
// 判断不是不post方式提交数据
if ($request->post()) {
// todo 数据验证
Menu::add($request->input());
return $this->response->responseJSON();
}
$data['menu'] = $category;
return view('admin.menu.addChild', $data);
}
在菜单视图目录下,新建一个addChild.blade.php文件。
子菜单添加成功
最后是删除,删除要注意一个问题就是,因为权限和菜单有关联关系,所以,删除菜单的话,那么被删除菜单下的子菜单以及权限都要删除,如果权限被删除的话,角色权限关系表中的数据也要删除,这样才能保持数据的一致性。
在菜单模型中,新写一个del方法
public static function del(array $ids)
{
return Menu::whereIn('id', $ids)->delete();
}
在菜单控制中,也新写一个del方法
public function del(Request $request)
{
$delRet = Menu::del($request->input('ids'));
if (!$delRet) {
$this->response->setMsg(500, '删除失败');
return $this->response->responseJSON();
}
return $this->response->responseJSON();
}
这个没有页面,是一个ajax请求,所以不需要视图。