Introduction
Controlling user access is a key part of building secure web applications. In Laravel 12, you can easily manage this using the Spatie Laravel Permission package.
This step-by-step tutorial will guide beginners through:
-
✅ Installing and setting up Spatie in a Laravel 12 project
-
👥 Creating roles like Admin, Editor, and User
-
🔐 Assigning permissions such as create, edit, delete, and view
-
🔒 Restricting access in your Blade templates — no Livewire needed!
Whether you're building an admin dashboard or a multi-user platform, this guide will help you set up clean and powerful access control.
Step 1: Install Laravel 12
Start by creating a fresh Laravel 12 project. Open your terminal and run:
composer create-project laravel/laravel example-app
Step 2: Install Spatie Permission Package
To manage roles and permissions, install the Spatie package:
composer require spatie/laravel-permission
Then, publish the package’s configuration and migration files:
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Update .env
to connect with your database:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel_db DB_USERNAME=root DB_PASSWORD=
Run migrations tables:
php artisan migrate
Step 3: Create Products Table
Generate a migration for the products
table:
php artisan make:model Product -m
Edit the migration file:
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('detail');
$table->timestamps();
});
Run the migration:
php artisan migrate
Step 4: Create Models
Update the User
Model to use roles:
app/Models/User.php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasRoles;
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}
Create the Product
model:
app/Models/Product.php
class Product extends Model
{
protected $fillable = ['name', 'detail'];
}
Step 5: Register Middleware
Add Spatie’s role/permission middleware in bootstrap/app.php
:
$middleware->alias([
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
]);
Step 6: Set Up Authentication
Install Laravel UI and generate auth scaffolding:
composer require laravel/ui php artisan ui bootstrap --auth
Then compile the frontend:
npm install npm run build
Step 7: Define Routes
Add routes in routes/web.php
for user, role, and product management:
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\RoleController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ProductController;
// Redirect root URL to /home if logged in, or to login otherwise
Route::get('/', function () {
if (Auth::check()) {
return redirect()->route('home');
}
return redirect()->route('login'); // or return view('welcome');
});
// Auth routes (login, register, forgot password, etc.)
Auth::routes();
// Home page after login
Route::get('/home', [HomeController::class, 'index'])->name('home');
// Protected routes (only accessible when logged in)
Route::middleware(['auth'])->group(function () {
Route::resource('roles', RoleController::class);
Route::resource('users', UserController::class);
Route::resource('products', ProductController::class);
});
Step 8: Create Controller
8.1. Create UserController
Run the following Artisan commands:
php artisan make:controller UserController
Create the file app/Http/Controllers/UserController.php
With the following content:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use Spatie\Permission\Models\Role;
use DB;
use Hash;
use Illuminate\Support\Arr;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class UserController extends Controller
{
public function index(Request $request): View
{
$data = User::latest()->paginate(5);
return view('users.index', compact('data'))
->with('i', ($request->input('page', 1) - 1) * 5);
}
public function create(): View
{
$roles = Role::pluck('name', 'name')->all();
return view('users.create', compact('roles'));
}
public function store(Request $request): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|same:confirm-password',
'roles' => 'required'
]);
$input = $request->all();
$input['password'] = Hash::make($input['password']);
$user = User::create($input);
$user->assignRole($request->input('roles'));
return redirect()->route('users.index')
->with('success', 'User created successfully');
}
public function show($id): View
{
$user = User::find($id);
return view('users.show', compact('user'));
}
public function edit($id): View
{
$user = User::find($id);
$roles = Role::pluck('name', 'name')->all();
$userRole = $user->roles->pluck('name', 'name')->all();
return view('users.edit', compact('user', 'roles', 'userRole'));
}
public function update(Request $request, $id): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email,' . $id,
'password' => 'same:confirm-password',
'roles' => 'required'
]);
$input = $request->all();
if (!empty($input['password'])) {
$input['password'] = Hash::make($input['password']);
} else {
$input = Arr::except($input, array('password'));
}
$user = User::find($id);
$user->update($input);
DB::table('model_has_roles')->where('model_id', $id)->delete();
$user->assignRole($request->input('roles'));
return redirect()->route('users.index')
->with('success', 'User updated successfully');
}
public function destroy($id): RedirectResponse
{
User::find($id)->delete();
return redirect()->route('users.index')
->with('success', 'User deleted successfully');
}
}
8.2. Create ProductController
Run the following Artisan commands:
php artisan make:controller ProductController
Create the file app/Http/Controllers/ProductController.php
With the following content:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class ProductController extends Controller
{
function __construct()
{
$this->middleware('permission:product-list|product-create|product-edit|product-delete', ['only' => ['index','show']]);
$this->middleware('permission:product-create', ['only' => ['create','store']]);
$this->middleware('permission:product-edit', ['only' => ['edit','update']]);
$this->middleware('permission:product-delete', ['only' => ['destroy']]);
}
public function index(): View
{
$products = Product::latest()->paginate(5);
return view('products.index', compact('products'))
->with('i', (request()->input('page', 1) - 1) * 5);
}
public function create(): View
{
return view('products.create');
}
public function store(Request $request): RedirectResponse
{
request()->validate([
'name' => 'required',
'detail' => 'required',
]);
Product::create($request->all());
return redirect()->route('products.index')
->with('success', 'Product created successfully.');
}
public function show(Product $product): View
{
return view('products.show', compact('product'));
}
public function edit(Product $product): View
{
return view('products.edit', compact('product'));
}
public function update(Request $request, Product $product): RedirectResponse
{
request()->validate([
'name' => 'required',
'detail' => 'required',
]);
$product->update($request->all());
return redirect()->route('products.index')
->with('success', 'Product updated successfully');
}
public function destroy(Product $product): RedirectResponse
{
$product->delete();
return redirect()->route('products.index')
->with('success', 'Product deleted successfully');
}
}
8.3. Create RoleController
Run the following Artisan commands:
php artisan make:controller RoleController
Create the file app/Http/Controllers/RoleController.php
With the following content:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use DB;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class RoleController extends Controller
{
function __construct()
{
$this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['index','store']]);
$this->middleware('permission:role-create', ['only' => ['create','store']]);
$this->middleware('permission:role-edit', ['only' => ['edit','update']]);
$this->middleware('permission:role-delete', ['only' => ['destroy']]);
}
public function index(Request $request): View
{
$roles = Role::orderBy('id', 'DESC')->paginate(5);
return view('roles.index', compact('roles'))
->with('i', ($request->input('page', 1) - 1) * 5);
}
public function create(): View
{
$permission = Permission::get();
return view('roles.create', compact('permission'));
}
public function store(Request $request): RedirectResponse
{
$this->validate($request, [
'name' => 'required|unique:roles,name',
'permission' => 'required',
]);
$permissionsID = array_map(function ($value) {
return (int)$value;
}, $request->input('permission'));
$role = Role::create(['name' => $request->input('name')]);
$role->syncPermissions($permissionsID);
return redirect()->route('roles.index')
->with('success', 'Role created successfully');
}
public function show($id): View
{
$role = Role::find($id);
$rolePermissions = Permission::join("role_has_permissions", "role_has_permissions.permission_id", "=", "permissions.id")
->where("role_has_permissions.role_id", $id)
->get();
return view('roles.show', compact('role', 'rolePermissions'));
}
public function edit($id): View
{
$role = Role::find($id);
$permission = Permission::get();
$rolePermissions = DB::table("role_has_permissions")->where("role_has_permissions.role_id", $id)
->pluck('role_has_permissions.permission_id', 'role_has_permissions.permission_id')
->all();
return view('roles.edit', compact('role', 'permission', 'rolePermissions'));
}
public function update(Request $request, $id): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'permission' => 'required',
]);
$role = Role::find($id);
$role->name = $request->input('name');
$role->save();
$permissionsID = array_map(function ($value) {
return (int)$value;
}, $request->input('permission'));
$role->syncPermissions($permissionsID);
return redirect()->route('roles.index')
->with('success', 'Role updated successfully');
}
public function destroy($id): RedirectResponse
{
DB::table("roles")->where('id', $id)->delete();
return redirect()->route('roles.index')
->with('success', 'Role deleted successfully');
}
}
Step 9: Add Blade Files
Create the main layout file
Update the file:
resources/views/layouts/app.blade.php
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.bunny.net"> <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet"> <!-- Scripts --> @vite(['resources/sass/app.scss', 'resources/js/app.js']) <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" /> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> StarCode Kh </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav me-auto"></ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ms-auto"> <!-- Authentication Links --> @guest @if (Route::has('login')) <li class="nav-item"> <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a> </li> @endif @if (Route::has('register')) <li class="nav-item"> <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a> </li> @endif @else <li><a class="nav-link" href="{{ route('users.index') }}">Manage Users</a></li> <li><a class="nav-link" href="{{ route('roles.index') }}">Manage Role</a></li> <li><a class="nav-link" href="{{ route('products.index') }}">Manage Product</a></li> <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} </a> <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> <div class="container"> <div class="row justify-content-center"> <div class="col-md-12"> <div class="card"> <div class="card-body"> @yield('content') </div> </div> </div> </div> </div> </main> </div> </body> </html>
resources/views/users/index.blade.php
@extends('layouts.app') @section('content') <div class="row mb-3"> <div class="col-lg-12 d-flex justify-content-between align-items-center"> <h2 class="mb-0">Users Management</h2> @can('role-create') <a class="btn btn-success btn-sm" href="{{ route('users.create') }}"> <i class="fa fa-plus"></i> Create New User </a> @endcan </div> </div> @session('success') <div class="alert alert-success" role="alert"> {{ $value }} </div> @endsession <table class="table table-bordered"> <tr> <th>No</th> <th>Name</th> <th>Email</th> <th>Roles</th> <th width="280px">Action</th> </tr> @foreach ($data as $key => $user) <tr> <td>{{ ++$i }}</td> <td>{{ $user->name }}</td> <td>{{ $user->email }}</td> <td> @if(!empty($user->getRoleNames())) @foreach($user->getRoleNames() as $v) <label class="badge bg-success">{{ $v }}</label> @endforeach @endif </td> <td> <a class="btn btn-info btn-sm" href="{{ route('users.show',$user->id) }}"> <i class="fa-solid fa-list"></i> Show </a> <a class="btn btn-primary btn-sm" href="{{ route('users.edit',$user->id) }}"> <i class="fa-solid fa-pen-to-square"></i> Edit </a> <form method="POST" action="{{ route('users.destroy', $user->id) }}" style="display:inline"> @csrf @method('DELETE') <button type="submit" class="btn btn-danger btn-sm"> <i class="fa-solid fa-trash"></i> Delete </button> </form> </td> </tr> @endforeach </table> {!! $data->links('pagination::bootstrap-5') !!} @endsection
resources/views/users/create.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Create New User</h2> </div> <div class="pull-right"> <a class="btn btn-primary btn-sm mb-2" href="{{ route('users.index') }}"> <i class="fa fa-arrow-left"></i> Back </a> </div> </div> </div> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form method="POST" action="{{ route('users.store') }}"> @csrf <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Name:</strong> <input type="text" name="name" placeholder="Name" class="form-control"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Email:</strong> <input type="email" name="email" placeholder="Email" class="form-control"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Password:</strong> <input type="password" name="password" placeholder="Password" class="form-control"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Confirm Password:</strong> <input type="password" name="confirm-password" placeholder="Confirm Password" class="form-control"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Role:</strong> <select name="roles[]" class="form-control" multiple="multiple"> @foreach ($roles as $value => $label) <option value="{{ $value }}"> {{ $label }} </option> @endforeach </select> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12 text-center"> <button type="submit" class="btn btn-primary btn-sm mt-2 mb-3"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/users/edit.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb d-flex justify-content-between align-items-center"> <h2>Edit User</h2> <a class="btn btn-primary btn-sm" href="{{ route('users.index') }}"> <i class="fa fa-arrow-left"></i> Back </a> </div> </div> @if ($errors->any()) <div class="alert alert-danger mt-2"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form method="POST" action="{{ route('users.update', $user->id) }}"> @csrf @method('PUT') <div class="row mt-3"> <div class="col-12 mb-3"> <label><strong>Name:</strong></label> <input type="text" name="name" class="form-control" placeholder="Name" value="{{ old('name', $user->name) }}"> </div> <div class="col-12 mb-3"> <label><strong>Email:</strong></label> <input type="email" name="email" class="form-control" placeholder="Email" value="{{ old('email', $user->email) }}"> </div> <div class="col-12 mb-3"> <label><strong>Password:</strong></label> <input type="password" name="password" class="form-control" placeholder="Password"> </div> <div class="col-12 mb-3"> <label><strong>Confirm Password:</strong></label> <input type="password" name="confirm-password" class="form-control" placeholder="Confirm Password"> </div> <div class="col-12 mb-3"> <label><strong>Role:</strong></label> <select name="roles[]" class="form-control" multiple> @foreach ($roles as $value => $label) <option value="{{ $value }}" {{ isset($userRole[$value]) ? 'selected' : '' }}> {{ $label }} </option> @endforeach </select> </div> <div class="col-12 text-center"> <button type="submit" class="btn btn-primary btn-sm"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/users/show.blade.php
@extends('layouts.app') @section('content') <div class="row mb-3"> <div class="col-lg-12 d-flex justify-content-between align-items-center"> <h2>Show User</h2> <a class="btn btn-primary btn-sm" href="{{ route('users.index') }}">Back</a> </div> </div> <div class="row"> <div class="col-12 mb-3"> <strong>Name:</strong> <p>{{ $user->name }}</p> </div> <div class="col-12 mb-3"> <strong>Email:</strong> <p>{{ $user->email }}</p> </div> <div class="col-12"> <strong>Roles:</strong><br> @if($user->getRoleNames()->isNotEmpty()) @foreach ($user->getRoleNames() as $role) <span class="badge badge-success">{{ $role }}</span> @endforeach @else <p>No Roles Assigned</p> @endif </div> </div> @endsection
resources/views/roles/index.blade.php
@extends('layouts.app') @section('content') <div class="row mb-3"> <div class="col-lg-12 d-flex justify-content-between align-items-center"> <h2>Role Management</h2> @can('role-create') <a class="btn btn-success btn-sm" href="{{ route('roles.create') }}"> <i class="fa fa-plus"></i> Create New Role </a> @endcan </div> </div> @if(session('success')) <div class="alert alert-success"> {{ session('success') }} </div> @endif <table class="table table-bordered"> <thead> <tr> <th width="100px">No</th> <th>Name</th> <th width="280px">Action</th> </tr> </thead> <tbody> @foreach ($roles as $key => $role) <tr> <td>{{ $loop->iteration }}</td> <td>{{ $role->name }}</td> <td> <a class="btn btn-info btn-sm" href="{{ route('roles.show', $role->id) }}"> <i class="fa-solid fa-list"></i> Show </a> @can('role-edit') <a class="btn btn-primary btn-sm" href="{{ route('roles.edit', $role->id) }}"> <i class="fa-solid fa-pen-to-square"></i> Edit </a> @endcan @can('role-delete') <form method="POST" action="{{ route('roles.destroy', $role->id) }}" class="d-inline"> @csrf @method('DELETE') <button type="submit" class="btn btn-danger btn-sm"> <i class="fa-solid fa-trash"></i> Delete </button> </form> @endcan </td> </tr> @endforeach </tbody> </table> {!! $roles->links('pagination::bootstrap-5') !!} @endsection
resources/views/roles/create.blade.php
@extends('layouts.app') @section('content') <div class="row mb-3"> <div class="col-lg-12 d-flex justify-content-between align-items-center"> <h2>Create New Role</h2> <a class="btn btn-primary btn-sm" href="{{ route('roles.index') }}"> <i class="fa fa-arrow-left"></i> Back </a> </div> </div> @if ($errors->any()) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form method="POST" action="{{ route('roles.store') }}"> @csrf <div class="row"> <div class="col-12 mb-3"> <label><strong>Name:</strong></label> <input type="text" name="name" class="form-control" placeholder="Name" value="{{ old('name') }}"> </div> <div class="col-12 mb-3"> <label><strong>Permission:</strong></label><br> @foreach($permission as $perm) <label> <input type="checkbox" name="permission[{{ $perm->id }}]" value="{{ $perm->id }}" class="me-1"> {{ $perm->name }} </label><br> @endforeach </div> <div class="col-12 text-center"> <button type="submit" class="btn btn-primary btn-sm"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/roles/edit.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Edit Role</h2> </div> <div class="pull-right"> <a class="btn btn-primary btn-sm mb-2" href="{{ route('roles.index') }}"><i class="fa fa-arrow-left"></i> Back</a> </div> </div> </div> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form method="POST" action="{{ route('roles.update', $role->id) }}"> @csrf @method('PUT') <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Name:</strong> <input type="text" name="name" placeholder="Name" class="form-control" value="{{ $role->name }}"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Permission:</strong> <br/> @foreach($permission as $value) <label> <input type="checkbox" name="permission[{{$value->id}}]" value="{{$value->id}}" class="name" {{ in_array($value->id, $rolePermissions) ? 'checked' : '' }}> {{ $value->name }} </label> <br/> @endforeach </div> </div> <div class="col-xs-12 col-sm-12 col-md-12 text-center"> <button type="submit" class="btn btn-primary btn-sm mb-3"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/roles/show.blade.php
@extends('layouts.app') @section('content') <div class="row mb-4"> <div class="col-lg-12 d-flex justify-content-between align-items-center"> <h2>Show Role</h2> <a class="btn btn-primary" href="{{ route('roles.index') }}">Back</a> </div> </div> <div class="row"> <div class="col-md-12 mb-3"> <div class="form-group"> <strong>Name:</strong> {{ $role->name }} </div> </div> <div class="col-md-12"> <div class="form-group"> <strong>Permissions:</strong> @if (!empty($rolePermissions)) @foreach ($rolePermissions as $permission) <span class="badge bg-success">{{ $permission->name }}</span> @endforeach @else <span>No permissions assigned.</span> @endif </div> </div> </div> @endsection
resources/views/products/index.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb d-flex justify-content-between align-items-center mb-3"> <h2>Products</h2> @can('product-create') <a class="btn btn-success btn-sm" href="{{ route('products.create') }}"> <i class="fa fa-plus"></i> Create New Product </a> @endcan </div> </div> @if ($message = Session::get('success')) <div class="alert alert-success" role="alert"> {{ $message }} </div> @endif @php $i = ($products->currentPage() - 1) * $products->perPage(); @endphp <div class="table-responsive"> <table class="table table-bordered"> <tr> <th>No</th> <th>Name</th> <th>Details</th> <th width="280px">Action</th> </tr> @foreach ($products as $product) <tr> <td>{{ ++$i }}</td> <td>{{ $product->name }}</td> <td>{{ $product->detail }}</td> <td> <form action="{{ route('products.destroy',$product->id) }}" method="POST"> <a class="btn btn-info btn-sm" href="{{ route('products.show',$product->id) }}"> <i class="fa-solid fa-list"></i> Show </a> @can('product-edit') <a class="btn btn-primary btn-sm" href="{{ route('products.edit',$product->id) }}"> <i class="fa-solid fa-pen-to-square"></i> Edit </a> @endcan @csrf @method('DELETE') @can('product-delete') <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')"> <i class="fa-solid fa-trash"></i> Delete </button> @endcan </form> </td> </tr> @endforeach </table> </div> {!! $products->links() !!} @endsection
resources/views/products/create.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Add New Product</h2> </div> <div class="pull-right"> <a class="btn btn-primary btn-sm" href="{{ route('products.index') }}"> <i class="fa fa-arrow-left"></i> Back </a> </div> </div> </div> @if ($errors->any()) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="{{ route('products.store') }}" method="POST"> @csrf <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Name:</strong> <input type="text" name="name" class="form-control" placeholder="Name"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Detail:</strong> <textarea class="form-control" style="height:150px" name="detail" placeholder="Detail"></textarea> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12 text-center"> <button type="submit" class="btn btn-primary btn-sm mb-3 mt-2"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/products/edit.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Edit Product</h2> </div> <div class="pull-right"> <a class="btn btn-primary btn-sm mb-2" href="{{ route('products.index') }}"> <i class="fa fa-arrow-left"></i> Back </a> </div> </div> </div> @if ($errors->any()) <div class="alert alert-danger"> <strong>Whoops!</strong> There were some problems with your input.<br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif <form action="{{ route('products.update', $product->id) }}" method="POST"> @csrf @method('PUT') <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Name:</strong> <input type="text" name="name" value="{{ $product->name }}" class="form-control" placeholder="Name"> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Detail:</strong> <textarea class="form-control" style="height:150px" name="detail" placeholder="Detail">{{ $product->detail }}</textarea> </div> </div> <div class="col-xs-12 col-sm-12 col-md-12 text-center"> <button type="submit" class="btn btn-primary btn-sm mb-2 mt-2"> <i class="fa-solid fa-floppy-disk"></i> Submit </button> </div> </div> </form> @endsection
resources/views/products/show.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Show Product</h2> </div> <div class="pull-right"> <a class="btn btn-primary" href="{{ route('products.index') }}">Back</a> </div> </div> </div> <div class="row mt-3"> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Name:</strong> {{ $product->name }} </div> </div> <div class="col-xs-12 col-sm-12 col-md-12"> <div class="form-group"> <strong>Details:</strong> {{ $product->detail }} </div> </div> </div> @endsection
Step 10: Create Seeder For Permissions and AdminUser
In this step, we create seeders to add fixed permissions and an admin user with the Admin role.
1. Create Permission Seeder
Run this command to generate the seeder:
php artisan make:seeder PermissionTableSeeder
Then, add this code to database/seeders/PermissionTableSeeder.php
:
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
class PermissionTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$permissions = [
'role-list',
'role-create',
'role-edit',
'role-delete',
'product-list',
'product-create',
'product-edit',
'product-delete'
];
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
}
}
Run the seeder with:
php artisan db:seed --class=PermissionTableSeeder
2. Create Admin User Seeder
Generate the seeder:
php artisan make:seeder CreateAdminUserSeeder
Add this code to database/seeders/CreateAdminUserSeeder.php
:
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class CreateAdminUserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$user = User::create([
'name' => 'Admin',
'email' => 'admin@gmail.com',
'password' => bcrypt('12345678')
]);
$role = Role::create(['name' => 'Admin']);
$permissions = Permission::pluck('id','id')->all();
$role->syncPermissions($permissions);
$user->assignRole([$role->id]);
}
}
Run the seeder with:
php artisan db:seed --class=CreateAdminUserSeeder
3. Run Laravel App
Finally, start your Laravel server:
php artisan serve
Visit your app at http://localhost:8000