Laravel 10.x karkasas. Naudotojų rolės

Laravel karkasas neturi naudotojų rolių, todėl jas reikia pasidaryti pačiam. Įvairūs programuotojai siūlo nemažai sprendimų, tačiau šiuo metu vienas populiariausių būdų tai padaryti yra naudoti Spatie Laravel Permision paketą. Paketo įdiegimui naudokite komandą:
composer require spatie/laravel-permission
Įsidiegus paketą reikia sugeneruoti migracijos ir konfigūracijos failus, tam įvykdykite komandą:
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Įvykdžius šią komandą kataloge ../library/databse/migrations/ atsiras failas *_create_permission_tables.php, o kataloge ../library/config/ - failas permission.php. Šiame konfigūracijos faile galima nustatyti įvairius paketo parametrus.
Naudojant Spatie Laravel Permision paketą rolės gali būti priskirtos įvairiems modeliams, tačiau mes naudosim tik User modelį, todėl atsidarykite ../library/config/permission.php failą ir eilutes:
'model_has_permissions' => 'model_has_permissions',
...
'model_has_roles' => 'model_has_roles',
...
'model_morph_key' => 'model_id',
pakeiskite į:
'model_has_permissions' => 'user_has_permissions',
...
'model_has_roles' => 'user_has_roles',
...
'model_morph_key' => 'user_id',
Įvykdykite lentelių migraciją į duomenų bazę:
php artisan migrate
Įvykdžius lentelių migraciją duomenų bazėje atsiras penkios naujos lentelės: roles, permissions, user_has_roles, user_has_permissions ir role_has_permissions. Naudosime tik dvi iš jų: roles ir user_has_roles.
Visos penkios lentelės automatiškai yra susiejamos ryšiais, tačiau jos nėra susiejamos su users lentele, todėl sukurkime migraciją set_permission_foreign:
php artisan make:migration set_permission_foreign
Šios migracijos metode up() įrašykite:
Schema::table('user_has_roles', function (Blueprint $table) {
    $table->foreign('user_id')->references('id')->on('users');
});

Schema::table('user_has_permissions', function (Blueprint $table) {
    $table->foreign('user_id')->references('id')->on('users');
});
Tada įvykdykite šią migraciją:
php artisan migrate
Nustačius ryšius duomenų bazės struktūra turėtų atrodyti taip:
Spatie permissions
Dabar atsidarykite modelį User ir prie naudojamų klasių įrašykite:
use Spatie\Permission\Traits\HasRoles;
Taip pat klasės viduje po eilute use TwoFactorAuthenticatable; įrašykite eilutę:
use HasRoles;
Sukurkite du naujus valdiklius Users ir Roles:
php artisan make:controller Admin/UsersController --resource
php artisan make:controller Admin/RolesController --resource
Valdiklio UsersController failo struktūra:
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;

class UsersController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $users = User::all();
        return view('admin.users.index', compact('users'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $roles = Role::pluck('name', 'id');
        $roles->prepend('---Please select---', 0);
        $roles->all();

        return view('admin.users.form', compact('roles'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $user = User::create($request->all());
        $user->assignRole($request->roles);

        return redirect('admin/users')->with('success', 'User added successfully.');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $user = User::findOrFail($id);
        return view('admin.users.show', compact('user'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $user = User::findOrFail($id);

        $roles = Role::pluck('name', 'id');
        $roles->prepend('---Please select---', 0);
        $roles->all();

        $selected_roles[] = array();
        foreach ($user->roles as $role) {
            $selected_roles[] = $role->id;
        }

        return view('admin.users.form', compact('user', 'roles', 'selected_roles'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $user = User::findOrFail($id);
        $user->update($request->all());
        $user->syncRoles($request->roles);

        return redirect('admin/users')->with('success', 'User updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $user = User::findOrFail($id);
        $user->delete();

        return redirect('admin/users')->with('success', 'User deleted successfully.');
    }
}
Valdiklio RolesController failo struktūra:
<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;

class RolesController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $roles = Role::all();
        return view('admin.roles.index', compact('roles'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('admin.roles.form');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        Role::create($request->all());

        return redirect('admin/roles')->with('success', 'Role added successfully.');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $role = Role::findOrFail($id);
        return view('admin.roles.show', compact('role'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $role = Role::findOrFail($id);
        return view('admin.roles.form', compact('role'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $role = Role::findOrFail($id);
        $role->update($request->all());

        return redirect('admin/roles')->with('success', 'Role updated successfully.');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $role = Role::findOrFail($id);
        $role->delete();

        return redirect('admin/roles')->with('success', 'Role deleted successfully.');
    }
}
Dabar sukurkime vaizdus naudotojams ir rolėms. Iš pradžių kataloge ../library/resources/views/admin/ sukurkite katalogus users ir roles, o juose failus index.blade.php, show.blade.php ir form.blade.php.
Failo ../users/index.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Users')

@section('content')
    <div class="card">
        <div class="card-header">
            <a href="{{ url('admin/users/create') }}" class="btn btn-primary"><i class="fas fa-plus"></i> Add user</a>
        </div>
        <div class="card-body">
            @if(Session::has('success'))
            <div class="alert alert-success alert-dismissible fade show" role="alert">
                {{ Session::get('success') }}
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
                @php
                    Session::forget('success');
                @endphp
            </div>
            @endif

            <div class="table-responsive">
                <table class="table table-bordered table-striped">
                    <thead>
                    <tr>
                        <th>ID</th>
                        <th>Name</th>
                        <th>E-mail</th>
                        <th>Role</th>
                        <th>Actions</th>
                    </tr>
                    </thead>
                    <tbody>
                    @foreach($users as $user)
                        <tr>
                            <td>{{ $user->id }}</td>
                            <td>{{ $user->name }}</td>
                            <td><a href="mailto:{{ $user->email }}">{{ $user->email }}</a></td>
                            <td>@foreach($user->roles as $role) {{ $role->name }} @endforeach</td>
                            <td>
                                <a href="{{ url('admin/users/'.$user->id.'/edit') }}" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i> Edit</a>
                                <a href="{{ url('admin/users/'.$user->id) }}" class="btn btn-success btn-sm"><i class="fas fa-eye"></i> View</a>
                                {!! Form::open(['method'=>'DELETE', 'url' => ['admin/users', $user->id], 'style' => 'display:inline']) !!}
                                {!! Form::button('<i class="fas fa-trash-alt"></i> Delete', ['class' => 'btn btn-danger btn-sm', 'type' => 'submit']) !!}
                                {!! Form::close() !!}
                            </td>
                        </tr>
                    @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>
@endsection
Failo ../users/show.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Users')

@section('content')
    <div class="card">
        <div class="card-header">
            <a href="{{ url('/admin/users/'.$user->id.'/edit') }}" class="btn btn-primary"><i class="fas fa-edit"></i> Edit user</a>
        </div>
        <div class="card-body">
            <div class="table-responsive">
                <table class="table table-bordered">
                    <tbody>
                    <tr>
                        <td>ID</td>
                        <td>{{ $user->id }}</td>
                    </tr>
                    <tr>
                        <td>Name</td>
                        <td>{{ $user->name }}</td>
                    </tr>
                    <tr>
                        <td>E-mail</td>
                        <td><a href="mailto:{{ $user->email }}">{{ $user->email }}</a></td>
                    </tr>
                    <tr>
                        <td>Role</td>
                        <td>@foreach($user->roles as $role) {{ $role->name }} @endforeach</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
@endsection
Failo ../users/form.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Users')

@section('content')
    <div class="card">
        <div class="card-header">
            <h6 class="m-0 font-weight-bold text-primary">
                @if(isset($user))
                    Edit exist user
                @else
                    Create new user
                @endif
            </h6>
        </div>
        <div class="card-body">
            @if(isset($user))
                {!! Form::model($user, ['url' => ['admin/users', $user->id], 'method' => 'patch', 'class' => 'needs-validation']) !!}
            @else
                {!! Form::open(['url' => 'admin/users', 'class' => 'needs-validation']) !!}
            @endif

            <div class="form-group">
                {!! Form::label('name', 'Name: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!! Form::text('name', null, ['class' => 'form-control'.($errors->has('name') ? ' is-invalid' : ''), 'required' => 'required']) !!}
                    {!! $errors->first('name', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>
            <div class="form-group">
                {!! Form::label('email', 'E-mail: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!! Form::email('email', null, ['class' => 'form-control'.($errors->has('email') ? ' is-invalid' : ''), 'required' => 'required']) !!}
                    {!! $errors->first('email', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>
            @if(!isset($user))
            <div class="form-group">
                {!! Form::label('password', 'Password: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!! Form::password('password', ['class' => 'form-control'.($errors->has('password') ? ' is-invalid' : ''), 'required' => 'required']) !!}
                    {!! $errors->first('password', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>
            <div class="form-group">
                {!! Form::label('password_confirmation', 'Confirm password: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!! Form::password('password_confirmation', ['class' => 'form-control'.($errors->has('password_confirmation') ? ' is-invalid' : ''), 'required' => 'required']) !!}
                    {!! $errors->first('password_confirmation', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>
            @endif
            <div class="form-group">
                {!! Form::label('role_id', 'Role: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!!Form::select('role_id', $roles, isset($user) ? $selected_roles : null, ['class' => 'form-control', 'multiple' => 'multiple', 'name' => 'roles[]', 'required' => 'required'])!!}
                    {!! $errors->first('role_id', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>

            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-3">
                    {!! Form::submit('Save', ['class' => 'btn btn-primary form-control']) !!}
                </div>
            </div>
            {!! Form::close() !!}
        </div>
    </div>
@endsection
Failo ../roles/index.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Roles')

@section('content')
    <div class="card">
        <div class="card-header">
            <a href="{{ url('admin/roles/create') }}" class="btn btn-primary"><i class="fas fa-plus"></i> Add role</a>
        </div>
        <div class="card-body">
            @if(Session::has('success'))
            <div class="alert alert-success alert-dismissible fade show" role="alert">
                {{ Session::get('success') }}
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
                @php
                    Session::forget('success');
                @endphp
            </div>
            @endif

            <div class="table-responsive">
                <table class="table table-bordered table-striped">
                    <thead>
                    <tr>
                        <th>ID</th>
                        <th>Role</th>
                        <th>Actions</th>
                    </tr>
                    </thead>
                    <tbody>
                    @foreach($roles as $role)
                        <tr>
                            <td>{{ $role->id }}</td>
                            <td>{{ $role->name }}</td>
                            <td>
                                <a href="{{ url('admin/roles/'.$role->id.'/edit') }}" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i> Edit</a>
                                <a href="{{ url('admin/roles/'.$role->id) }}" class="btn btn-success btn-sm"><i class="fas fa-eye"></i> View</a>
                                {!! Form::open(['method'=>'DELETE', 'url' => ['admin/roles', $role->id], 'style' => 'display:inline']) !!}
                                {!! Form::button('<i class="fas fa-trash-alt"></i> Delete', ['class' => 'btn btn-danger btn-sm', 'type' => 'submit']) !!}
                                {!! Form::close() !!}
                            </td>
                        </tr>
                    @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>
@endsection
Failo ../roles/show.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Role')

@section('content')
    <div class="card">
        <div class="card-header">
            <a href="{{ url('/admin/roles/'.$role->id.'/edit') }}" class="btn btn-primary"><i class="fas fa-edit"></i> Edit role</a>
        </div>
        <div class="card-body">
            <div class="table-responsive">
                <table class="table table-bordered">
                    <tbody>
                    <tr>
                        <td>ID</td>
                        <td>{{ $role->id }}</td>
                    </tr>
                    <tr>
                        <td>Role</td>
                        <td>{{ $role->name }}</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
@endsection
Failo ../roles/form.blade.php struktūra:
@extends('layouts.admin')

@section('title', 'Roles')

@section('content')
    <div class="card">
        <div class="card-header">
            <h6 class="m-0 font-weight-bold text-primary">
                @if(isset($role))
                    Edit exist role
                @else
                    Create new role
                @endif
            </h6>
        </div>
        <div class="card-body">
            @if(isset($role))
                {!! Form::model($role, ['url' => ['admin/roles', $role->id], 'method' => 'patch', 'class' => 'needs-validation']) !!}
            @else
                {!! Form::open(['url' => 'admin/roles', 'class' => 'needs-validation']) !!}
            @endif

            <div class="form-group">
                {!! Form::label('name', 'Name: ', ['class' => 'col-sm-3']) !!}
                <div class="col-sm-6">
                    {!! Form::text('name', null, ['class' => 'form-control'.($errors->has('name') ? ' is-invalid' : ''), 'required' => 'required']) !!}
                    {!! $errors->first('name', '<div class="invalid-feedback">:message</div>') !!}
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-3">
                    {!! Form::submit('Save', ['class' => 'btn btn-primary form-control']) !!}
                </div>
            </div>
            {!! Form::close() !!}
        </div>
    </div>
@endsection
Atsidarykite maršrutų failą ir virš eilutės Auth::routes(); įterpkite šiuos maršrutus:
Route::get('/admin/users', [App\Http\Controllers\Admin\UsersController::class, 'index']);
Route::get('/admin/users/create', [App\Http\Controllers\Admin\UsersController::class, 'create']);
Route::post('/admin/users', [App\Http\Controllers\Admin\UsersController::class, 'store']);
Route::get('/admin/users/{id}', [App\Http\Controllers\Admin\UsersController::class, 'show']);
Route::get('/admin/users/{id}/edit', [App\Http\Controllers\Admin\UsersController::class, 'edit']);
Route::patch('/admin/users/{id}', [App\Http\Controllers\Admin\UsersController::class, 'update']);
Route::delete('/admin/users/{id}', [App\Http\Controllers\Admin\UsersController::class, 'destroy']);

Route::get('/admin/roles', [App\Http\Controllers\Admin\RolesController::class, 'index']);
Route::get('/admin/roles/create', [App\Http\Controllers\Admin\RolesController::class, 'create']);
Route::post('/admin/roles', [App\Http\Controllers\Admin\RolesController::class, 'store']);
Route::get('/admin/roles/{id}', [App\Http\Controllers\Admin\RolesController::class, 'show']);
Route::get('/admin/roles/{id}/edit', [App\Http\Controllers\Admin\RolesController::class, 'edit']);
Route::patch('/admin/roles/{id}', [App\Http\Controllers\Admin\RolesController::class, 'update']);
Route::delete('/admin/roles/{id}', [App\Http\Controllers\Admin\RolesController::class, 'destroy']);
Atsidarykite nuorodą http://localhost:8000/admin/roles ir sukurkite roles admin, librarian ir member.
Atsidarykite failą ../library/app/Actions/Fortify/CreateNewUser.php, suraskite metodą create() ir jame esantį kodą
return $user = User::create([
    'name' => $data['name'],
    'email' => $data['email'],
    'password' => Hash::make($data['password']),
]);
pakeiskite į
$user = User::create([
    'name' => $data['name'],
    'email' => $data['email'],
    'password' => Hash::make($data['password']),
]);
return $user->assignRole('member');
Paskutinė eilutė automatiškai kiekvienam naujai užregistruotam naudotojui pridės rolę member.
Atsidarykite failą ../library/resources/views/layouts/admin.blade.php ir navigacijoje pridėkite porą nuorodų:
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
    ...
    @role('admin')
    <li class="nav-item">
        <a href="{{ url('/admin/users') }}" class="nav-link">
            <i class="nav-icon fas fa-user"></i>
            <p>Users</p>
        </a>
    </li>
    <li class="nav-item">
        <a href="{{ url('/admin/roles') }}" class="nav-link">
            <i class="nav-icon fas fa-key"></i>
            <p>Roles</p>
        </a>
    </li>
    @endrole
</ul>
Administravimo panelėje atsiras du nauji meniu punktai: Users ir Roles, kuriuos matys tik naudotojas, priskirtas admin rolei.
Nuo šiol galite priskirti naudotojams įvairias roles. Tačiau dar liko nesutvarkyti maršrutai, todėl bet kuris naudotojas gali patekti į bet kurį puslapį. Pereikite prie sekančio skyriaus Maršrutai, kad sužinoti kaip tai atlikti.

Daugiau apie naudotojų roles skaitykite oficialioje Spatie Laravel Permission dokumentacijoje.
Taip pat rekomenduoju peržiūrėti šias video pamokas, kad sužinotumėte daugiau Spatie Laravel Permission paketo galimybių:
Spatie Laravel Permission Package Tutorial | Introduction #1
Spatie Laravel Permission Package Tutorial | User Role and Permission #2
Spatie Laravel Permission Package Tutorial | Explaining user role and permission together #3
Spatie Laravel Permission Package Tutorial | Using Middleware for permission and role #4
Spatie Laravel Permission Package Tutorial | User Role and Permission #5

Paskutinį kartą puslapis keistas 2023-04-24

© Joana Katina 2016-2024. Visos teisės saugomos