搜索
您的当前位置:首页正文

Laravel从入门到上线运营-12RBAC-03篇

来源:哗拓教育

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

这里涉及到 laravelDB::raw,这方法的目的就是执行原生的sql语句。 whereJsonContainslaravel 提供的 json 查询方法, json_removejson 字段的删除,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 是更新操作,经过以上的修改 rolepermission 的绑定已经修改完成

# 添加修改后台用户

再做一下 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 文件中将 roleadmin 做一对一绑定。之前我们已经写好了一半。

<?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。后面的参数就是各自对应的字段。具体可以看文档。直接访问属性的方式访问方法名 rolelaravel 就会自动帮你查询并绑定。

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 篇到此结束。

Top