I would recommend watching the whole Laravel In 30 dyas series, and particulary this on about Authorization.
https://laracasts.com/series/30-days-to-learn-laravel-11/episodes/23
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
Excuse me, I'm a junior developer and I'm still learning about the Laravel gates concept. I have a table structure like this.
I have two admin users. One real admin and one normal admin.
Real admin has access to all options. But the normal admin only has access to the dashboard.
@can('dashboard')
<a>dashboard</a>
@endcan
@can('users')
<a>users</a>
@endcan
A normal admin should only see the dashboard, but he can see everything.
AuthServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use App\Models\Permission;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Log;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
$this->registerPolicies();
Gate::before(function($user) {
if($user->isAdmin()) return true;
});
foreach (Permission::all() as $permission) {
Gate::define($permission->name , function($user) use ($permission){
return $user->hasPermission($permission);
});
}
}
}
User.php
public function isAdmin()
{
return $this->level == 'admin' ? true : false;
}
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
public function hasRole($roles)
{
return !! $roles->intersect($this->roles)->all() ;
}
public function hasPermission($permission)
{
return $this->permissions->contains('name' , $permission->name) || $this->hasRole($permission->roles);
}
tables
public function up(): void
{
Schema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('label')->nullable();
$table->timestamps();
});
Schema::create('permission_user' , function(Blueprint $table) {
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->primary(['permission_id', 'user_id']);
});
}
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('label')->nullable();
$table->timestamps();
});
Schema::create('role_user', function (Blueprint $table) {
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->primary(['role_id', 'user_id']);
});
Schema::create('permission_role', function (Blueprint $table) {
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->primary(['permission_id', 'role_id']);
});
}
Any idea what it's trying to get at?
When I add this line
@dump(auth()->user()->permissions)
I get this message.
Illuminate\Database\Eloquent\Collection {#1450 ▼ // resources\views/Admin/layouts/sidebar.blade.php
#items: array:1 [▼
0 => App\Models\Permission {#1453 ▼
#connection: "mysql"
#table: "permissions"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:7 [▼
"id" => 1
"user_id" => 1
"name" => "dashboard"
"published" => 1
"label" => "Dashboard"
"created_at" => "2024-04-30 09:01:47"
"updated_at" => "2024-05-01 15:52:54"
]
#original: array:9 [▼
"id" => 1
"user_id" => 1
"name" => "dashboard"
"published" => 1
"label" => "Dashboard"
"created_at" => "2024-04-30 09:01:47"
"updated_at" => "2024-05-01 15:52:54"
"pivot_user_id" => 2
"pivot_permission_id" => 1
]
#changes: []
#casts: []
#classCastCache: []
#attributeCastCache: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"pivot" => Illuminate\Database\Eloquent\Relations\Pivot {#1451 ▼
#connection: "mysql"
#table: "permission_user"
#primaryKey: "id"
#keyType: "int"
+incrementing: false
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:2 [▼
"user_id" => 2
"permission_id" => 1
]
#original: array:2 [▼
"user_id" => 2
"permission_id" => 1
]
#changes: []
#casts: []
#classCastCache: []
#attributeCastCache: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: false
+usesUniqueIds: false
#hidden: []
#visible: []
#fillable: []
#guarded: []
+pivotParent: App\Models\User {#1422 ▼
#connection: "mysql"
#table: "users"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
+preventsLazyLoading: false
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#escapeWhenCastingToString: false
#attributes: array:9 [▼
"id" => 2
"level" => "admin"
"name" => "normal admin"
"email" => "[email protected]"
"email_verified_at" => null
"password" => "y$IlcmjFTrQXwUxy.caqpJ6ez7LuXBaJRkegXqnWaaCFoUYBdhS7Eoy"
"remember_token" => "SYqm0kVkfLho1truJEyEQ9sjOG7TyZfzGvRdoLkkP5491QivskO5BvuV0JQd"
"created_at" => "2024-05-01 15:54:24"
"updated_at" => "2024-05-01 15:54:24"
]
#original: array:9 [▼
"id" => 2
"level" => "admin"
"name" => "normal admin"
"email" => "[email protected]"
"email_verified_at" => null
"password" => "y$IlcmjFTrQXwUxy.caqpJ6ez7LuXBaJRkegXqnWaaCFoUYBdhS7Eoy"
"remember_token" => "SYqm0kVkfLho1truJEyEQ9sjOG7TyZfzGvRdoLkkP5491QivskO5BvuV0JQd"
"created_at" => "2024-05-01 15:54:24"
"updated_at" => "2024-05-01 15:54:24"
]
#changes: []
#casts: array:2 [▼
"email_verified_at" => "datetime"
"password" => "hashed"
]
#classCastCache: []
#attributeCastCache: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: array:1 [▼
"permissions" => Illuminate\Database\Eloquent\Collection {#1450}
]
#touches: []
+timestamps: true
+usesUniqueIds: false
#hidden: array:2 [▼
0 => "password"
1 => "remember_token"
]
#visible: []
#fillable: array:3 [▼
0 => "name"
1 => "email"
2 => "password"
]
#guarded: array:1 [▼
0 => "*"
]
#rememberTokenName: "remember_token"
#accessToken: null
}
#foreignKey: "user_id"
#relatedKey: "permission_id"
}
]
#touches: []
+timestamps: true
+usesUniqueIds: false
#hidden: []
#visible: []
#fillable: []
#guarded: []
}
]
#escapeWhenCastingToString: false
}
Please or to participate in this conversation.