Eloquent Many to Many select User without certain Roles - laravel

So I have User & Role models with many-to-many relationship, I have 3 roles: super, admin and moderator with 4 users let's says: John, Mike, James and Larry.
John is a super, Mike has admin and moderator roles, James is an admin and Larry is a moderator. To displaying users who doesn't have certain roles I created this scope:
public function scopeDoesntHaveRoles($query, $roles = [], $column = 'id') {
return $query->whereDoesntHave('roles')->orWhereHas('roles', function ($q) use ($roles, $column) {
$q->whereNotIn($column, $roles);
});
}
When I call User::doesntHaveRoles([1])->lists('name', 'id') to get users who doesn't have super role, it works and returns:
{"2":"Mike","3":"James","4":"Larry"}
But, when I trying to list users who doesn't have admin role User::doesntHaveRoles([2])->lists('name', 'id'), yes James is not shown there but Mike is appeared while he is actually has admin role:
{"1":"John","2":"Mike","4":"Larry"}
I think it's because Mike is also has moderator role, do you see something wrong in my scope? or do you have other solutions?
Thanks
Edit:
Here is my pivot schema
Schema::create('user_roles', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('role_id')->unsigned();
$table->primary([
'user_id', 'role_id'
]);
});
User model
public function roles()
{
return $this->belongsToMany(Role::class, 'user_roles');
}
Role Model
public function users()
{
return $this->belongsToMany(User::class, 'user_roles');
}

I would use whereNotIn instead of whereDoesntHave.
Given a Role stored in the variable $role you can get all users who don't have that role with:
/* #var Role $role */
User::whereNotIn(function('id', $query) use ($role) {
$query->select('user_id')
->from('user_roles')
->where('role_id', $role->id);
});
The inner query will return all IDs of users who has the given role. Using whereNotIn will return the opposite set of users. The folowing query will be created:
select *
from users
where user_id not in (
select user_id
from user_roles
where role_id = ?
);
Now having a Collection of roles stored in $roles you can get all users who don't have any of that roles with:
/* #var Collection|Role[] $roles */
User::whereNotIn(function('id', $query) use ($roles) {
$query->select('user_id')
->from('user_roles')
->whereIn('role_id', $roles->pluck('id');
});
The inner select will return IDs of all users who has one of the roles in the collection. With whereNotIn you will again get the opposite result. You can also use an array of role-ids instead of $roles->pluck('id').
The builder will create a query like
select *
from users
where user_id not in (
select user_id
from user_roles
where role_id in (?, ?, ..)
);

Related

Get user that belongs to a specific role in laravel

I have following table for roles:
id | name | user_id
1 Admin NULL
2 Accountant 1
3 Storekeeper 1
and user table like this:
id | name | role_id
1 Admin 1
2 xyz 2
3 xyz 3
I want to retrieve all data from users table that have been created by Admin in my case that have user_id 1.
I'm using this relation:
public function Role()
{
return $this->hasMany('App\Models\Role', 'user_id', 'id');
}
$users = User::with('Role')->whereHas('Role', function($query) {
$query->where('user_id', 1);
})->get();
but this is not returning data from users table it is returning rows from roles table only that have user_id 1.
larar
From your database structure, the relation user to role is belongsTo not hasMany
each user has one role.
each role can be assigned to many users.
User ==Belongs to==> Role
Role ==Has many==> User
public function role()
{
return $this->belongsTo('App\Models\Role', 'role_id', 'id');
}
And then you call the users with admin role like this.
$adminsWithRole = User::with('role')->whereHas('Role', function($query) {
$query->where('roles.id', 1);
})->get();
Lets say you want to get the Created roles by the users that are admin
This needs its own relation
public function createdRoles()
{
return $this->hasMany('App\Models\Role', 'user_id', 'id');
}
The get the created roles by all users that have the role admin
$adminsWithCreatedRoles = User::with('createdRoles')->whereHas('Role', function($query) {
$query->where('roles.id', 1);
})->get();

Fetch data that don't exist in pivot table column - Laravel 6

I have two tables viz
1. users
columns: id, name
2. user_access
columns: id, user_id, user_access_id
user access is a pivot table that defines the relationship between users table and users table itself. For example, a user might have access to data of some other users.
I have defined this relationship in User model.
Now I want to fetch those user ids for a user which don't exist in user_access table.
For example, a user may have access to id 1 & 2 but he doesn't have access to id 3 & 4, so I want to fetch id 3 & 4 and not 1 & 2.
To achieve this I use whereDoesntHave eloquent but it's not working for relationship on the same tables and I get the following error
Facade\Ignition\Exceptions\ViewException
Call to undefined method Illuminate\Database\Eloquent\Builder::getRelated() (View: C:\xampp\htdocs\prd_tracker\resources\views\tracker\ticket\script.blade.php)
but it does work for different tables.
Here is my code
User Model
public function userAccess()
{
return $this->belongsToMany('App\User', 'user_access', 'user_id', 'user_access_id')->using('App\USER_ACCESS');
}
Logic
use App\User;
$user_id = Auth::user()->id;
//Get All Those Users Ids Which Does Not Belong To Auth User
$exception_user_ids = User::whereDoesntHave('users', function ($query) use($user_id) {
$query->where('user_id', $user_id);
})
->pluck('id');
The error may be in the User model.
The App\User model cannot belongsToMany() of itself...App\User.
I believe you may need a UserAccess model.
Try:
User Model
public function userAccess()
{
return $this->belongsToMany('App\UserAccess', 'user_access', 'user_id', 'user_access_id')
}
or try a hasMany relationship:
User Model
public function userAccess()
{
return $this->hasMany('App\UserAccess', 'user_id', 'id');
}
https://laravel.com/docs/7.x/eloquent-relationships#many-to-many
https://laravel.com/docs/7.x/eloquent-relationships#one-to-many

Use whereHas to find all items that doesn't have specific value (for example User and Role - get all user that does not have admin role)

I have two models, User, and Role. Each User has many roles like 'admin', 'simple-user' and so on.
I want to get all user that doesn't have 'admin' role. I know this can be solved by the Join query. But I want use "wherHas()" function.
User table has id, name
Role table has id, slug, name
user_role as pivot table has user_id and role_id
In the User model
public function roles()
{
return $this->belongsToMany('App\Role');
}
and in the Role model
public function users()
{
return $this->belongsToMany('App\User');
}
Example query:
$users = User::whereHas('roles', function ($query){
$query->where('slug', '!=', 'ecoyar');
})->get();
sorry about my english!
How to query the absence of a relationship:
use Illuminate\Database\Eloquent\Builder;
$users = App\User::whereDoesntHave('roles', function (Builder $query) {
$query->where('slug', 'like', 'ecoyar');
})->get();

Laravel relationship within relationship to another table

I have users table, user_roles table and roles table.
My user_roles table:
id, role_id, user_id
My roles table:
id, name, description, created_at, updated_at
My users table:
id, first_name, last_name, user_name, avatar, account_status, email, password, remember_token, created_at, updated_at, deleted_at
Now I am trying to get the logged in user with the role including the role description, but cannot seem to get it working.
In my user model I have this:
public function roles()
{
return $this->hasOne(User_roles::class, 'role_id', 'id');
}
This gives me the User_role but no description. I tried belongsToMany, but this gave me the Roles values and pivot with the User_roles table valus. Can I do it vice versa so I get the User_roles table values and pivot will be the description of the Roles table?
BelongsToMany relationship at the moment:
public function roles()
{
return $this->belongsToMany(Roles::class, 'user_roles', 'id', 'id');
}
Use hasManyThrough relation instead:
https://laravel.com/docs/5.6/eloquent-relationships#has-many-through
This way you can specify the intermediate user_role table
You should try to below solution
In User Model
public function roles()
{
return $this->belongsToMany(Role::class, 'user_roles');
}
In Controller
$user = User::find($user_id);
$roles = $user->roles;
In User Model
public function roles()
{
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
}

Laravel: how order by many to many table?

I have tables
users
id
username
role
id
name
users_role
user_id
role_id
I have users listing and I want ability to sort by username and role_id, how I can do this?
users
1:first
2:second
3:third
roles
1:admin
2:customer
users_role
1:1
1:2
2:2
3:2
Sort can be by first user role_id(or for max role_id -> if user have roles 1 and 2 sort by 2)
First of all, you need to create relationships for two model: User and Role (i suppose that we have three tables: users, roles and role_user:
class User extends Model
{
public function roles() {
return $this->belongsToMany('\App\Role');
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('\App\User');
}
}
Next, with this query you can have list of users and roles, sort by username ASC and role_id DESC
\App\User::with(array('roles' => function($query) {
$query->orderBy('role_id', 'DESC');
}))
->orderBy('username', 'ASC')
->get();

Resources