Hello, laravel web developers! In this tutorial, we’ll build a Multi-Role CMS in Laravel 11 using custom policies to manage user roles such as Admin, Editor, and Author. Policies allow us to control user access dynamically and give specific permissions based on roles.
Once the policy class has been registered, you may add methods for each action it authorizes. If you used the --model
option when generating your policy via the Artisan console, it will already contain methods for the viewAny
, view
, create
, update
, delete
, restore
, and forceDelete
actions.
Laravel 11 Building a Multi-Role CMS with Custom Policies
Prerequisites:
php artisan make:auth
or Jetstream)
We'll install the laravel 11 application using the following command in this step.
composer create-project laravel/laravel laravel-11-example
We’ll start by defining user roles and assigning them permissions. For simplicity, we'll store user roles directly in the users
table.
Add Role Field to Users Table
php artisan make:migration add_role_to_users_table --table=users
Migration:
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('author'); // Default role set as 'author'
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
Now, run the migration:
php artisan migrate
We’ll define roles in the User
model as constants.
app/Models/User.php
class User extends Authenticatable
{
const ROLE_ADMIN = 'admin';
const ROLE_EDITOR = 'editor';
const ROLE_AUTHOR = 'author';
// Check role methods for convenience
public function isAdmin()
{
return $this->role === self::ROLE_ADMIN;
}
public function isEditor()
{
return $this->role === self::ROLE_EDITOR;
}
public function isAuthor()
{
return $this->role === self::ROLE_AUTHOR;
}
}
Next, we'll use Laravel Policies to authorize user actions based on their roles. Create a Policy for the Post
Model.
php artisan make:policy PostPolicy --model=Post
Edit the generated policy to include methods that define access based on user roles.
app/Policies/PostPolicy.php
class PostPolicy
{
// Only Admins and Editors can view all posts
public function viewAny(User $user)
{
return $user->isAdmin() || $user->isEditor();
}
// Only Admins, Editors, and Authors can view individual posts
public function view(User $user, Post $post)
{
return $user->isAdmin() || $user->isEditor() || $user->isAuthor();
}
// Only Admins and Editors can create new posts
public function create(User $user)
{
return $user->isAdmin() || $user->isEditor();
}
// Only Admins and Editors can update posts
public function update(User $user, Post $post)
{
return $user->isAdmin() || $user->isEditor();
}
// Only Admins can delete posts
public function delete(User $user, Post $post)
{
return $user->isAdmin();
}
}
In AuthServiceProvider
, register the policy to link it with the Post
model.
app/Providers/AuthServiceProvider.php
use App\Models\Post;
use App\Policies\PostPolicy;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Post::class => PostPolicy::class,
];
public function boot()
{
$this->registerPolicies();
}
}
With the policies defined, let's use them in the PostController.
app/Http/Controllers/PostController.php
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
// Show all posts (Admin/Editor only)
public function index()
{
$this->authorize('viewAny', Post::class);
$posts = Post::all();
return view('posts.index', compact('posts'));
}
// Show single post (All roles)
public function show(Post $post)
{
$this->authorize('view', $post);
return view('posts.show', compact('post'));
}
// Create a new post (Admin/Editor only)
public function create()
{
$this->authorize('create', Post::class);
return view('posts.create');
}
// Store the post
public function store(Request $request)
{
$this->authorize('create', Post::class);
// Validation and saving logic
}
// Edit a post (Admin/Editor only)
public function edit(Post $post)
{
$this->authorize('update', $post);
return view('posts.edit', compact('post'));
}
// Update the post
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// Validation and updating logic
}
// Delete a post (Admin only)
public function destroy(Post $post)
{
$this->authorize('delete', $post);
// Delete logic
}
}
In your Blade views, use the @can
directive to control the visibility of actions based on roles.
posts/index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Building a Multi-Role CMS with Custom Policies in Laravel 11 - Techsolutionstuff</title>
</head>
<body>
<h2>Laravel 11 Building a Multi-Role CMS with Custom Policies - Techsolutionstuff</h2>
@foreach ($posts as $post)
<div class="post">
<h3>{{ $post->title }}</h3>
<p>{{ $post->body }}</p>
<!-- Show Edit button if the user can update the post -->
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan
<!-- Show Delete button if the user can delete the post -->
@can('delete', $post)
<form action="{{ route('posts.destroy', $post) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit">Delete</button>
</form>
@endcan
</div>
@endforeach
</body>
</html>
If you want to restrict certain routes based on roles, you can create a custom middleware. Create Middleware using the following command.
php artisan make:middleware CheckRole
app/Http/Middleware/CheckRole.php
public function handle($request, Closure $next, $role)
{
if (!auth()->check() || auth()->user()->role !== $role) {
abort(403, 'Unauthorized action.');
}
return $next($request);
}
Register Middleware in the app.php file:
bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'role' => \App\Http\Middleware\CheckRole::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Use Middleware in Routes. Now, you can restrict routes based on roles like this:
routes/web.php
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('posts', PostController::class);
});
Now, run the laravel 11 application using the following command.
php artisan serve
You might also like: