How to check if user is Admin or not in laravel blade view without calling DB multiple times?
Is there a way how to pass it in generic way to all views?
I have relationships, middleware and all defined. This is DB structure:
user
table:
- id
- name
role
table:
- id
- name
user_role
table:
- id
- user_id
- role_id
Note: DB structure has to be like that, I dont want to have to have IsAdmin flag on user table as I am not planning to add new column everytime new role is added.
Currently what I am doing in views is pretty basic:
View:
@if(auth()->user()->IsAdministrator()))
..
@endif
User model:
public function isAdministator()
{
return $this->roles()->where('role_id', 1)->exists();
}
public function roles()
{
return $this->belongsToMany(Role::class);
}
Which is fine and it is working, but the problem is, when I need to do this multiple times on the page:
- to show Edit button only to Admins
- to make Delete button visible only to Admins
- in navigation menu, to show specific menu items only for Admins
Everytime I am checking for IsAdministrator(), it is making a DB querry for same thing.
- I was thinking about passing this from Controller, but to all Controllers?
- I also thought to add contructructor to base controller: Controller.php but getting NULL for auth()->user()
- Perhaps using below, but it is not working as I am getting NULL as well, when I am trying to pass $IsAdmin = auth()-user()-IsAdministrator() https://laravel.com/docs/10.x/views#sharing-data-with-all-views
Is there good practice on this?
Note 2: same question was asked by user w1n78, but it was lost in between other answers: https://stackoverflow.com/a/47082188/8009914
2
Answers
In
eloquent
inLaravel
, accessing query builders as so$this->roles()
will always create aSQL
query to the database when that code is called.If you utilize the relationship loading that
eloquent
automatically does, it will not perform multiple queries.The only downside is with the query builder you get the power of SQL functions. With the eloquent relationship approach you are limited to the Collection class representing an array of relationship models in
Laravel
.Instead of using
auth()->user() -> ...
in your view multiple times, define a variable and pass it to your views:Note: This can be done in a Controller to return to a Single view, or in
View Composer
for all views, etc.This will allow you to call
$user->isAdministrator()
in your views. Currently, this is the same as your existing code, but we can now make this more performant, since the User and it’s relationships can be loaded into a variable.First, load your
roles
relationship:Now,
$user->roles
can be accessed without needing an additional query. As pointed out correctly by mrhn,$user->roles()
will ALWAYS execute a new query, regardless if the Relationship has been loaded or not.In your
isAdministrator()
method, adjust your code:Now, in your view, check your method via:
If all is done properly, you should be able to call
$user->isAdministrator()
, and/or other->is{Whatever}()
methods multiple times without trigging any additional queries.Sidenote, in later versions of Laravel, you can force this behaviour by triggering an Exception when you try to access a relationship that isn’t loaded:
https://laravel.com/docs/10.x/eloquent-relationships#preventing-lazy-loading
If you included that code, you would be able to remove the
if (!$this->relationLoaded('role')) { ... }
section of yourisAdministrator()
method, as it would be handled by the Application globally.