# json 类型
当我们对 permission
的数据做操作,比如删除,我们的role表也是需要做对应的删除,laravel
还没有对应的删除方法,支持 json 操作的方法很少,需要我们自己写原生的 sql
语句
修改 permission
的删除方法
public function destroy(Permission $permission)
{
Role::whereJsonContains('permission', $permission->name)->update(
['permission' => \DB::raw("json_remove(`permission`, json_unquote(json_search(`permission`, 'one', '$permission->name')))")]
);
$permission->delete();
return redirect('admin/permission');
}
这里涉及到 laravel
的 DB::raw
,这方法的目的就是执行原生的sql语句。 whereJsonContains
是 laravel
提供的 json
查询方法, json_remove
是 json
字段的删除,mysql
5.7 版本才有 json
类型的支持,同学们自行学习。再来改一下 permission
的更新方法
public function update($id, PermissionRequest $request)
{
$permission = Permission::where('id', $id)->firstOrfail();
Role::whereJsonContains('permission', $permission->name)->update(
['permission' => \DB::raw("json_set(`permission`,json_unquote(json_search(`permission`, 'one', '$permission->name')),'$request->name')")]
);
$permission->update($request->only('name', 'title'));
return redirect('admin/permission');
}
json_set
是更新操作,经过以上的修改 role
和 permission
的绑定已经修改完成
# 添加修改后台用户
再做一下 role
和用户的绑定,实际上就是在用户添加或修改的地方给他一个角色。创建路由
Route::middleware('auth:admin')->group(function () {
Route::resource('admin', 'AdminController')->except(['destroy']); //改,不做删除
Route::resource('role', 'RoleController');
Route::resource('permission', 'PermissionController');
});
AdminController
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminRequest;
use App\Model\Admin\Admin;
use App\Model\Admin\Role;
class AdminController extends Controller
{
private $data = [
'role_id' => 0,
'name' => '',
'super' => 0,
];
public function index()
{
$data = Admin::all();
return view('admin.admin.index', compact('data'));
}
public function create()
{
$old = [];
foreach ($this->data as $k => $datum) {
$old[$k] = old($k, $datum);
}
$roles = Role::pluck('title', 'id');
return view('admin.admin.create', compact('old', 'roles'));
}
public function store(AdminRequest $request)
{
Admin::create($request->only('name', 'role_id', 'super') + ['password' => bcrypt($request->password)]);
return redirect('admin/admin');
}
public function edit(Admin $admin)
{
$old = [];
foreach ($this->data as $k => $datum) {
$old[$k] = old($k, $admin->$k);
}
$old['uid'] = $admin->uid;
$roles = Role::pluck('title', 'id');
return view('admin.admin.edit', compact('old', 'roles'));
}
public function update($id, AdminRequest $request)
{
$data = $request->only('name', 'role_id', 'super');
if ($request->filled('password')) {
$data['password'] = ['password' => bcrypt($request->password)];
}
Admin::where('uid', $id)->update($data);
return redirect('admin/admin');
}
}
AdminRequest.php
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class AdminRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => ['required', 'alpha', $this->isMethod('post') ? 'unique:admins' : Rule::unique('admins')->ignore($this->admin, 'uid')],
'password' => $this->isMethod('post') ? 'required|confirmed' : 'confirmed',
'role_id' => 'integer',
'super' => 'integer',
];
}
public function attributes()
{
return [
'name' => '用户名',
'role_id' => '角色',
'super' => '超级管理员',
];
}
}
admin
模板文件
@extends('admin.app')
@section('content')
<div class="container-fluid mt-4">
<div class="row">
<div class="col-12 mb-3">
<a href="{{ url('admin/admin/create') }}" class="btn btn-success">+</a>
</div>
<div class="col-12">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">用户名</th>
<th scope="col">角色</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
@foreach($data as $v)
<tr>
<th scope="row">{{ $v->uid }}</th>
<td>{{ $v->name }}</td>
<td>{{ $v->role_id }}</td>
<td>
<a href="{{ route('admin.edit', $v->uid) }}" class="btn btn-primary">改</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
@csrf
<input type="text" name="name" class="form-control @error('name') is-invalid @enderror" placeholder="用户名" value="{{ $old['name'] }}" required>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
<input type="password" name="password" class="form-control mt-3 @error('password') is-invalid @enderror" placeholder="密码" @if($create??false) required @endif>
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
<input type="password" name="password_confirmation" class="form-control mt-3 @error('password_confirmation') is-invalid @enderror" placeholder="确认密码" @if($create??false) required @endif>
@error('password_confirmation')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
<input type="hidden" value="0" name="super">
<div class="form-group">
<div class="form-check">
<input class="form-check-input @error('super') is-invalid @enderror" type="checkbox" id="super" value="1" name="super" {{ $old['super'] ?'checked':'' }}>
<label class="form-check-label" for="super">超级管理员</label>
@error('super')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group">
<select class="custom-select @error('role_id') is-invalid @enderror" name="role_id">
<option value="0" selected>空角色</option>
@foreach ($roles as $k => $item)
<option value="{{$k}}" {{$k==$old['role_id']?'selected':''}}>{{$item}}</option>
@endforeach
</select>
@error('role_id')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
<button class="btn btn-block btn-primary mt-3">ok</button>
@extends('admin.app')
@section('title', '添加用户')
@section('content')
<div class="container-fluid mt-4">
<div class="row">
<div class="col-12">
<form action="{{ url('admin/admin') }}" method="post">
@include('admin.admin.form', ['create' => true])
</form>
</div>
</div>
</div>
@endsection
@extends('admin.app')
@section('title', '修改用户')
@section('content')
<div class="container-fluid mt-4">
<div class="row">
<div class="col-12">
<form action="{{ url('admin/admin', $old['uid']) }}" method="post">
@method('put')
@include('admin.admin.form')
</form>
</div>
</div>
</div>
@endsection
这里有几点,模板 @include
方法可以传值,form.blade
根据值可以判断是添加还是更新。super
字段加了一个隐藏 input
,当超级用户没有勾选时,依然会有值传到后台。
# 关联关系与 getAttribute
后台用户首页直接展示 role_id
不是很好,看不出来到底是什么角色。我们在 model
文件中将 role
与 admin
做一对一绑定。之前我们已经写好了一半。
<?php
namespace App\Model\Admin;
use Illuminate\Foundation\Auth\User;
class Admin extends User
{
protected $primaryKey = 'uid';
protected $guarded = [];
public function role() //方法名可以随意取名,不固定
{
return $this->hasOne(Role::class, 'id', 'role_id');
}
public function getRoleNameAttribute()
{
if ($this->role_id == 0) {
return '空角色';
}
return $this->role->title;
}
}
hasOne
方法就是说 admin
有一个 role
。后面的参数就是各自对应的字段。具体可以看文档。直接访问属性的方式访问方法名 role
,laravel
就会自动帮你查询并绑定。
getRoleNameAttribute
这个方法可以让模型使用属性的方式去访问,用小写字母和下划线的方式访问 role_name
就会得到这个值。
修改 blog/resources/views/admin/admin/index.blade.php
...
<table class="table table-bordered table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">用户名</th>
<th scope="col">角色名/超级管理员</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
@foreach($data as $v)
<tr>
<th scope="row">{{ $v->uid }}</th>
<td>{{ $v->name }}</td>
<td>
<span class="badge badge-success">{{ $v->role_name }}</span>
@if ($v->super)<span class="badge badge-danger">超</span>@endif
</td>
<td>
<a href="{{ route('admin.edit', $v->uid) }}" class="btn btn-primary">改</a>
</td>
</tr>
@endforeach
</tbody>
</table>
...
# 预加载
我们访问一次用户列表页,再看一下 sql
查询记录。如果有多个用户和角色存在,那就会有多次查询 role
表操作。
select * from `roles` where `roles`.`id` = 2 and `roles`.`id` is not null limit 1 82.64ms
select * from `roles` where `roles`.`id` = 2 and `roles`.`id` is not null limit 1 82.64ms
...
这样多次查询是不合理的,可以用 whereIn
的方式先把 role
全查出来,再用 php
去做匹配绑定。laravel
已经帮我们做好了,只需要调用 with
,填入对应的关系即可。
修改 index
方法
public function index()
{
$data = Admin::with('role')->get();
return view('admin.admin.index', compact('data'));
}
再去看看记录,sql语句是否已经减少。每次做完一个功能都要看看 sql
是否有多余,及时对其调整。 RBAC
篇到此结束。