
Laravel 8 User Roles & Permissions and Product CRUD With Images Tutorial
CRUD is an acronym that comes from the world of computer programming and refers to the four functions that are considered necessary to implement a persistent storage application: create, read, update and delete.
User management is important feature in any web application. A user should have access to the permissions that only required. That way, user can only see the menus that assigned to its role.
In this article, we are using spatie github package for roles and permissions in laravel 8 application. just follow bellow step to create acl in laravel 8.
Spatie role permission composer package provide way to create acl in laravel 8. they provide how to assign role to user, how to assign permission to user and how to assign permission assign to roles. i will write step by step creating roles and permissions in laravel 8 application.
Step 1: Create Laravel application
we need to create a new laravel application. for that open your terminal or command prompt and create a new Laravel application.
composer create-project laravel/laravel mynewapp
And than go to project directory
cd mynewapp
Step 2: Install require composer package
Now we nedd to install require Spatie package for ACL (Access Control List). Open your terminal and execute bellow command.
composer require spatie/laravel-permission
composer require laravelcollective/html
Now we need to register provider class to config/app.php file so open file and add service provider.
'providers' => [
Spatie\Permission\PermissionServiceProvider::class,
];
If we want to change on Spatie package then we need to run bellow command that will generate config/permission.php and migration files
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Step 3: Configure database connection
go to .env file and confiure database connection type, db host, db port, db database, db username, db password
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_usercontrol
DB_USERNAME=root
DB_PASSWORD=
now we can run migrate command
php artisan migrate
Step 4: Create product migration
we need to create product table for migration as using bellow command. This will generate CreateProductsTable class at database>migrations
php artisan make:migration create_products_table
products table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('detail');
$table->string('image');
$table->decimal('price', 8, 2);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
}
Step 5: Create model
In this step, we we will create a product model to get data from product table. Run the below artisan command to generate product model. That will generate Product model at app>Models
php artisan make:model Product
add fillable attribute in Product model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
protected $fillable = [
'name', 'detail', 'image', 'price'
];
}
Laravel have already User model. We just need to add HasRoles trait in it. So open User model and add it.
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasRoles;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Step 6: Register middlewares package
The package will handle roles and permission middlewares. We can add them inside app/Http/Kernel.php file.
protected $routeMiddleware = [
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
]
Step 7: Create authentication scaffold
Now we need to implement Laravel default authentication feature. So run the following command in Terminal.
composer require laravel/ui
We need views for login and register. We can do it with following command. This will generate view file for login and register.
php artisan ui bootstrap --auth

Now we also need to compile view assets for authentication view. So run the below npm command.
Note: The below npm commands only require for asset compiling, if you are adding your own view, then you don’t need to run npm commands.
npm install
npm run dev
Step 8: Create controllers
We are going to build CRUD part for users, roles and product table, so we need resource controllers for them. Run below commands one by one to create controllers.
php artisan make:controller UserController --resource
php artisan make:controller RoleController --resource
php artisan make:controller ProductController --resource
app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\User;
use Spatie\Permission\Models\Role;
use DB;
use Hash;
use Illuminate\Support\Arr;
class UserController extends Controller
{
function __construct()
{
// set permission
$this->middleware('permission:user-list|user-create|user-edit|user-delete', ['only' => ['index','show']]);
$this->middleware('permission:user-create', ['only' => ['create','store']]);
$this->middleware('permission:user-edit', ['only' => ['edit','update']]);
$this->middleware('permission:user-delete', ['only' => ['destroy']]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$data = User::orderBy('id','DESC')->paginate(5);
return view('users.index',compact('data'))->with('i', ($request->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$roles = Role::pluck('name','name')->all();
return view('users.create',compact('roles'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$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');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$user = User::find($id);
return view('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::find($id);
$roles = Role::pluck('name','name')->all();
$userRole = $user->roles->pluck('name','name')->all();
return view('users.edit',compact('user','roles','userRole'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$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');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
User::find($id)->delete();
return redirect()->route('users.index')->with('success','User deleted successfully');
}
}
app/Http/Controllers/RoleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use DB;
class RoleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
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']]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$roles = Role::orderBy('id','DESC')->paginate(5);
return view('roles.index',compact('roles'))->with('i', ($request->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$permission = Permission::get();
return view('roles.create',compact('permission'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|unique:roles,name',
'permission' => 'required',
]);
$role = Role::create(['name' => $request->input('name')]);
$role->syncPermissions($request->input('permission'));
return redirect()->route('roles.index')->with('success','Role created successfully');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$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'));
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$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'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'name' => 'required',
'permission' => 'required',
]);
$role = Role::find($id);
$role->name = $request->input('name');
$role->save();
$role->syncPermissions($request->input('permission'));
return redirect()->route('roles.index')->with('success','Role updated successfully');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
DB::table("roles")->where('id',$id)->delete();
return redirect()->route('roles.index')->with('success','Role deleted successfully');
}
}
app/Http/Controllers/ProductController.php
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
function __construct()
{
// set permission
$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']]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::latest()->paginate(5);
return view('products.index',compact('products'))->with('i', (request()->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('products.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required',
'detail' => 'required',
'price'=>'required',
'image' => 'required|image|mimes:jpg,png,jpeg,gif,svg|max:2048',
]);
$input = $request->all();
if ($image = $request->file('image')) {
$destinationPath = 'image/';
$productImage = date('YmdHis') . "." . $image->getClientOriginalExtension();
$image->move($destinationPath, $productImage);
$input['image'] = "$productImage";
}
Product::create($input);
return redirect()->route('products.index')->with('success','Product created successfully.');
}
/**
* Display the specified resource.
*
* @param \App\Product $product
* @return \Illuminate\Http\Response
*/
public function show(Product $product)
{
return view('products.show',compact('product'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Product $product
* @return \Illuminate\Http\Response
*/
public function edit(Product $product)
{
return view('products.edit',compact('product'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Product $product
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Product $product)
{
$request->validate([
'name' => 'required',
'detail' => 'required',
'price'=>'required',
]);
$input = $request->all();
if ($image = $request->file('image')) {
$destinationPath = 'image/';
$productImage = date('YmdHis') . "." . $image->getClientOriginalExtension();
$image->move($destinationPath, $productImage);
$input['image'] = "$productImage";
}else{
unset($input['image']);
}
$product->update($input);
return redirect()->route('products.index')->with('success','Product updated successfully');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Product $product
* @return \Illuminate\Http\Response
*/
public function destroy(Product $product)
{
$ImagePath = 'image/'.$product->image;
unset($ImagePath);
$product->delete();
return redirect()->route('products.index')->with('success','Product deleted successfully');
}
}
Step 9: Create routes
We have created all of controllers. Now we need to create routes for them, open routes/web.php and add new routes in it.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\RoleController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ProductController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', [HomeController::class, 'index'])->name('home');
Route::group(['middleware' => ['auth']], function() {
Route::resource('roles', RoleController::class);
Route::resource('users', UserController::class);
Route::resource('products', ProductController::class);
});
Step 10: Create view files
In this is step, we are going to create a blade view files. We will use one common layout and extend it to all views. Here is the list of all views that we will create in resources/views/
directory. You can go to all folder and copy each view to your local file. we need to create following files
home.blade.php
layouts/
app.blade.php
users/
index.blade.php
show.blade.php
create.blade.php
edit.blade.php
roles/
index.blade.php
show.blade.php
create.blade.php
edit.blade.php
products/
index.blade.php
show.blade.php
create.blade.php
edit.blade.php
Let’s start from common layout file. It is already there, you just slightly need to change in it and add route link in navigation bar.
resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>{{ config('app.name') }}</title>
<!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Custom styles for this template -->
<link href="navbar-static-top.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- Static navbar -->
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ url('/') }}">Laravel 8 User Roles & Permissions and Product CRUD With Images Tutorial</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="{{ url('/') }}">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@guest
<li><a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a></li>
<li><a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a></li>
@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="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ Auth::user()->name }}<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{{ route('users.show', Auth::id()) }}">Profile</a></li>
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</li>
</ul>
</li>
@endguest
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
@yield('content')
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</body>
</html>
resources/views/home.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection

complete users view balde file form https://github.com/robin-bd/laravel-8-CRUD-with-images/tree/main/resources/views/users
complete roles view balde file form https://github.com/robin-bd/laravel-8-CRUD-with-images/tree/main/resources/views/roles

complete products view balde file form https://github.com/robin-bd/laravel-8-CRUD-with-images/tree/main/resources/views/products
Step 11: Create Seeder
In this step we will create seeder for permissions, Right now we have fixed permission so we create using seeder as listed bellow, but if you can add more permission as you want:
php artisan make:seeder PermissionTableSeeder
And put bellow code in PermissionTableSeeder seeder this way:
database/seeds/PermissionTableSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
class PermissionTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$permissions = [
'role-list',
'role-create',
'role-edit',
'role-delete',
'product-list',
'product-create',
'product-edit',
'product-delete',
'user-list',
'user-create',
'user-edit',
'user-delete',
];
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
}
}
php artisan make:seeder CreateAdminUserSeeder
database/seeds/CreateAdminUserSeeder.php
<?php
namespace Database\Seeders;
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.
*
* @return void
*/
public function run()
{
$user = User::create([
'name' => 'Noor E Alam',
'email' => 'admin@email.com',
'password' => bcrypt('123456')
]);
$role = Role::create(['name' => 'Admin']);
$permissions = Permission::pluck('id','id')->all();
$role->syncPermissions($permissions);
$user->assignRole([$role->id]);
}
}
update DatabaseSeeder class at
database/seeds/CreateAdminUserSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
// call others seeder class
$this->call([
PermissionTableSeeder::class,
CreateAdminUserSeeder::class,
]);
}
}
run bellow code for database table migrate, seeding and run application server.
php artisan migrate
php artisan db:seed
php artisan serve
default email and password for admin access
email: admin@email.com
password: 123456
This full application are available on github. you can download it and run it. https://github.com/robin-bd/laravel-8-CRUD-with-images
Thanks for share. It is excellent. I have a question: if You create a role, then for link it to Permissions and authorize, you must do it writting code or it is authomatic?
Thank you for your time
It’s not automatic.