Laravel 10 User Roles and Permissions Tutorial Mousavi, June 17, 2023February 20, 2024 In this tutorial, I am going to talk about how to implement User, role, and Permission with the help of a famous Laravel package, which is called Laravel-Permission. This package allows you to manage user permissions and roles in a database. There are a considerable number are associated in using this package such as reliability, security. This package regularly update by the owner, so it is highly recommended by expertise to use this package in Laravel in order to implement role and permission. In this tutorial, I will create a project that has three different sections. User Manager Role Manager Post manager Step 1: create a brand new Laravel 10 project composer create-project --prefer-dist laravel/laravel Laravel-Permission-Tutorial Step 2: Install require packages . Run these command in order. composer require spatie/laravel-permission composer require laravelcollective/html composer require laravel/ui php artisan ui bootstrap –auth npm install npm run dev php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" Step 3: Run migration in order to create required tables. Before that don’t forget to set database values in .env file. php artisan migrate in order to make sure that tables are created. We can open phpmyadmin or relevant program .As we can see following tables has been created. Step 4: Now we can create our post model and migration. php artisan make:model -m Post open the migration file (2023_06_15_093440_create_posts_table.php) and change it to following content.(you may see different date and time!) Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->string('description'); $table->timestamps(); }); Then run php artisan migrate to create posts table in database. Open Post Model and add the following protected $fillable = [ 'title', 'description' ]; Step 5: Now we have to make some modifications in user model. Open the user model and change the use section as a following. use HasFactory, Notifiable, HasRoles; Step 6: Add middleware Open kernel.php in app/http and add the following text. 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 three controllers php artisan make:controller UserController –resource php artisan make:controller RoleController –resource php artisan make:controller PostController --resource Change UserController as a following code Each method is completely self- explanatory <?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 { /** * 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'); } } And for the role controller we can use following code <?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'); } } As you can see in the constructor, I use the middleware that I previously entered in kernel.php. For example in the first line in constructor I define that, for calling the index and store methods the user at least must has one of the role-list or role-create or role-edit or role-delete . Later I explain where to define these permissions. In addition, in the store method you can see we assign some specific permissions to a role. We use this technique to create dynamic roles and permissions so in order to define new role we do not have to change any code in our program. Finally, we go to the post controller and change it based on following code <?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Http\Request; class PostController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ function __construct() { $this->middleware('permission:post-list|post-create|post-edit|post-delete', ['only' => ['index','show']]); $this->middleware('permission:post-create', ['only' => ['create','store']]); $this->middleware('permission:post-edit', ['only' => ['edit','update']]); $this->middleware('permission:post-delete', ['only' => ['destroy']]); } /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $posts = post::latest()->paginate(5); return view('posts.index',compact('posts')) ->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('posts.create'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { request()->validate([ 'title' => 'required', 'description' => 'required', ]); post::create($request->all()); return redirect()->route('posts.index') ->with('success','post created successfully.'); } /** * Display the specified resource. * * @param \App\post $post * @return \Illuminate\Http\Response */ public function show(post $post) { return view('posts.show',compact('post')); } /** * Show the form for editing the specified resource. * * @param \App\post $post * @return \Illuminate\Http\Response */ public function edit(post $post) { return view('posts.edit',compact('post')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param \App\post $post * @return \Illuminate\Http\Response */ public function update(Request $request, post $post) { request()->validate([ 'title' => 'required', 'description' => 'required', ]); $post->update($request->all()); return redirect()->route('posts.index') ->with('success','post updated successfully'); } /** * Remove the specified resource from storage. * * @param \App\post $post * @return \Illuminate\Http\Response */ public function destroy(post $post) { $post->delete(); return redirect()->route('posts.index') ->with('success','post deleted successfully'); } } Like Rolecontroller we define our permission strategies in constructor. Step 8: in order to save time ,download the following view zip file and extract it to the resources/views viewsDownload Step 9: Now we need seed some data php artisan make:seeder PermissionTableSeeder In run method insert following data public function run(): void { $permissions = [ 'role-list', 'role-create', 'role-edit', 'role-delete', 'post-list', 'post-create', 'post-edit', 'post-delete' ]; foreach ($permissions as $permission) { Permission::create(['name' => $permission]); } } Now run Db seed php artisan db:seed --class=PermissionTableSeeder we need to define default admin password public function run(): void { $user = User::create([ 'name' => 'Mousavi.Dev', 'email' => 'admin@gmail.com', 'password' => bcrypt('123456') ]); $role = Role::create(['name' => 'Admin']); $permissions = Permission::pluck('id','id')->all(); $role->syncPermissions($permissions); $user->assignRole([$role->id]); } php artisan make:seeder CreateAdminUserSeeder public function run(): void { $user = User::create([ 'name' => 'Mousavi.Dev', 'email' => 'admin@gmail.com', 'password' => bcrypt('123456') ]); $role = Role::create(['name' => 'Admin']); $permissions = Permission::pluck('id','id')->all(); $role->syncPermissions($permissions); $user->assignRole([$role->id]); } php artisan db:seed --class=CreateAdminUserSeeder Step 10: Create Routes Open web.php and add following routes.Don’t forget to import namespace. Route::group(['middleware' => ['auth']], function() { Route::resource('roles', RoleController::class); Route::resource('users', UserController::class); Route::resource('Posts', PostController::class); }); Step 11: Run the project with following command php artisan serve Go to the login page and enter following username and password Email: admin@gmail.com Password:123456 Got the manage user section and you will see all the users of the website. In the Role management you can see all roles and all permission that assign to this role. For example I created reviewer role that only has permission to see all posts. Now I go to the user manager and create new user with this role Before login to the new user, we need to add some posts in Manage Post section Then log out and login with the revieweruser that we have already created As you can see we just only see user manager and post manager (pay attention to constructor of every controller!) . Now in post manager we only see Posts . this user cant add,remove or edit the post. you can download complete project at https://github.com/m-mosuavi/Laravel-Permission I hope this article come handy. Please leave a comment if you encounter any problem 🙂 Laravel/PHP laravellaravel-permissionsecurityspatie