Start
Permission design is a crucial feature for backend management systems. PHP already has many mature packages for this purpose, so we don’t need to reinvent the wheel (though you certainly can if preferred).
PS
Here are two common historical approaches for permission verification:
- Verify required permissions individually on each page
- Centralized verification via middleware
First, let’s examine a simplified database schema (retaining only critical fields) with its ER diagram:
Models
1. User Model
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* Relationship with Role model
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
/**
* Check if user has a specific permission
* 1. Get required permission name
* 2. Iterate through user's roles
* 3. Check if any role has the permission
*/
public function hasPermission($permissionName)
{
foreach ($this->roles as $role) {
if ($role->permissions()->where('name', $permissionName)->exists()) {
return true;
}
}
return false;
}
}
2. Role Model
<?php
namespace App\Models;
class Role extends Model
{
public function users()
{
return $this->belongsToMany(User::class);
}
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
}
3. Permission Model
<?php
namespace App\Models;
class Permission extends Model
{
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
Database Seeding
Sample data:
# Users
+----+-------+----------+
| id | name | password |
+----+-------+----------+
| 1 | gps | 123456 |
| 2 | david | 123456 |
# Roles
+----+-------+
| id | name |
+----+-------+
| 1 | admin |
# Permissions
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | create_product |
| 2 | delete_product |
# role_user (User gps has admin role)
+---------+---------+
| role_id | user_id |
+---------+---------+
| 1 | 1 |
# permission_role (Admin role has product permissions)
+---------+---------------+
| role_id | permission_id |
+---------+---------------+
| 1 | 1 |
| 1 | 2 |
Approach 1: Page-Level Verification
<?php
namespace App\Http\Controllers;
use App\Models\Product;
class ProductsController extends Controller
{
public function store(Request $request)
{
if (!$request->user()->hasPermission('create_product')) {
abort(403);
}
// Business logic
return back()->with('status', 'Product created');
}
public function destroy(Product $product)
{
if (!$request->user()->hasPermission('delete_product')) {
abort(403);
}
// Business logic
return back()->with('status', 'Product deleted');
}
}
Approach 2: Centralized Middleware Verification
Enhance the permissions table with a route
column:
# permissions
+----+-----------------+------------------+
| id | name | route |
+----+-----------------+------------------+
| 1 | create_product | products.store |
| 2 | delete_product | products.destroy |
Create a middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Route;
use App\Models\Permission;
class PermissionAuth
{
public function handle($request, Closure $next)
{
$route = Route::currentRouteName();
if ($permission = Permission::where('route', $route)->first()) {
if (!auth()->user()->hasPermission($permission->name)) {
return response()->view('errors.403', [
'status' => "Insufficient permissions. Required: {$permission->name}"
]);
}
}
return $next($request);
}
}
END
For Laravel implementations, consider using the mature package:
https://github.com/spatie/laravel-permission