Laravel - order by number of entries - php

This is a follow up from this question. I have a query that looks like this:
$users = User::leftjoin('role_user', 'users.id', '=', 'role_user.user_id')
->select('users.*')
->orderBy(DB::raw('role_id IS NULL'))
->groupBy('users.id')
->orderBy('role_id', 'asc')
->get();
When I do it like that I get distinct values for users, and they are ordered by role_id in asc order, the only thing with this query is that users that have two or more roles are behind the ones with who have only one role. For example, user has roles with id=1 and id=2, and other user has a only a role with id=1, that user will be before the user with two roles in the list. I would like to change that and have users with more roles above the ones with only one role.

I would recommend to use Query Scopes and Querying Relations
class User extends Authenticatable
{
...
public function roles()
{
return $this->belongsToMany('Role', 'role_user');
}
// Scope users who have a specified role
public function scopeHasRole($query, $role) {
return $query->whereHas('roles', function ($q) use ($role) {
$q->where('id', $role);
});
}
// Scope users who have a set of roles
public function scopeHasRoles($query, $roles) {
if (! is_array($roles)) {
return $this->scopeHasRole($query, $roles);
}
return $query->whereHas('roles', function ($q) use ($roles) {
$q->whereIn('id', $roles);
});
}
// Scope a user who don't have any roles.
public function scopeDoesntHaveAnyRoles($query) {
return $query->whereDoesntHave('roles');
}
}
Example usage:
User::doesntHaveAnyRoles(); // return users don't have any roles.
User::hasRole(1)->orderBy('id', 'asc')->get(); // return users who belongs to role id 1.
User::hasRoles([1, 2, 3]); // return users who have a set of roles.

Following query worked for me in the end:
User::leftjoin('role_user', 'users.id', '=', 'role_user.user_id')
->select('users.*')
->selectRaw('user_id, count(*) as count')
->orderBy(DB::raw('role_id IS NULL'))
->groupBy('users.id')
->orderBy('count', 'DESC')
->orderBy('role_id', 'asc')
->get();

Related

I can not select users that have departments in laravel

$users = User::with('departments')
->whereHas('departments', function ($q) use ($departments) {
return $q->whereIn('department_id', $departments);
})->whereNotNull('users.email')
->get();
public function users(){return $this->morphedByMany(User::class, 'departmentable');}
in department model
and
public function departments(){return $this->morphToMany(Department::class, 'departmentable');
}
in user model
in your whereIn statement you use department_id column in departments table, are you sure of this, isn't just 'id' ('departments.id')?
$users = User::with('departments')
->whereHas('departments', function ($q) use ($departments) {
return $q->whereIn('departments.id', $departments);
})->whereNotNull('users.email')
->get();

accessing relationship from model laravel

i have a two tables where user table has a one to one relationship with role table
this is my user model
public function role(){
return $this->belongsTo(Role::class);
}
this is my role model
public function user(){
return $this->hasOne(User::class);
}
this my table structure for role
this my table structure for user
how can i access in my user model the role which is equal to employee
i tried
User::with('role')->where('role_name','employee')->get();
but it has an error
role_name column not found
This will give you only the users which has an employee role .
User::whereHas('role', function ($query) {
$query->where('role_name', 'employee');
})->get();
And this will give you all the users with their employee roles if any exist
User::with(['role' => function ($query) {
$query->where('role_name', 'employee');
}])->get();
Try this:
User::whereHas('role', function ($query) {
$query->where('role_name', 'employee');
})->get();
Details at: https://laravel.com/docs/5.4/eloquent-relationships#querying-relationship-existence
Try this:
User::with(['role' => function ($query) {
$query->where('role_name', 'employee');
}])->get();

Getting all related fields to related field in Eloquent

I have this DB structure:
classroom_user is Elqouent many-to-many pivot.
User.php:
public function classrooms() {
return $this->belongsToMany(ClassRoom::class, 'classroom_user', 'classroom_id', 'user_id');
}
ClassRoom.php:
public function users()
{
return $this->belongsToMany(User::class, 'classroom_user', 'user_id', 'classroom_id');
}
I have a $user and want to write Eloquent method which gets all users who are in at least one of classrooms the $useralso is in. Something like $user->classrooms->users. But this falls with error Property [users] does not exist on this collection instance.. How can I do this?
I think you can do it like this:
$users = User::whereHas('classrooms', function($query) {
$query->whereHas('users', function($query) {
$query->where('id', auth()->user()->id);
});
})->get();
You could store this as a scope in your User model:
public function scopeClassmatesOf($query, $user)
{
$query->whereHas('classrooms', function($query) use($user) {
$query->whereHas('users', function($query) use($user) {
$query->where('id', $user->id);
});
})
}
And then call it from a controller like:
$user = auth()->user();
$users = User::classmatesOf($user)->get();
Your asking for users of a collection. $user->classrooms is a collection of classrooms.
If you do $user->classrooms->first()->users you will get all the users from the first classroom.
To get all users from all classrooms, you can do something like;
$users = $user->classrooms->map(function ($classroom) {
return $classroom->users;
});
Note that will return duplicate users and authenticated user him self witin the collection. So, you might need to do some filtering to the result.
This worked for me:
public function classmates() {
$self = $this;
$a = User::with('classrooms')->whereHas('classrooms', function ($query) use ($self) {
$query->whereHas('users', function ($query) use ($self) {
$query->where('users.id', $self->id);
});
});
return $a;
}

Laravel 5.1 constrain and eager load multiple nested relations

I have four models: OwnerCompany, Owner, User, and Role.
I want to get all OwnerCompanys eager loading their Owner and also eager loading its Users who have the Role with the name 'admin'.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();
This loads in the models but doesn't constrain the users, they are all loaded.
Not tested . I think you are missing like or equal operator.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name','like','admin');
})->get();
I try this, if work then let me know. not tested
1).
OwnerCompany::with('owner.users.roles')->whereHas('owner', function ($query) {
$query->whereHas('users', function($qr){
$qr->whereHas('roles', function($q){
$q->where('name', 'admin');
});
});
})->get();
OR
2).
OwnerCompany::with('owner.users.roles')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();
If I got it right, you want each OwnerCompany object to contain the Owner object, which will contain "admins". I would do something like this.
$ownerCompany = OwnerCompany::all();
return $ownerCompany->each(function ($company) {
$company->owner;
$company->owner->users->each(function ($user) {
return $user->hasRole('admin');
});
});
and inside User class:
public function role()
{
return $this->belongsTo(Role::class);
}
public function hasRole($role)
{
if ($this->role()->where('name', $role)->first()) {
return $this;
}
return null;
}
Unfortunately, I can't test it right now, so If you have any trouble let me know.

Removing item from collection change its type - Laravel

I'm using Laravel 5.4. This code retrieves users belonging to a company and return them to the client side.
The following query is used to get the user of the company
$users = User::where('company_id', '=', $idCompany)
->with([
'roles' => function($q) {
$q->whereBetween('level', [50, 999]);
}
])->get();
Situation A
return $users;
$users is an array of objects [{user1}, {user2}]
Situation B
foreach($users as $key=> $user) {
if(count($user->roles) == 0){$users->forget($key);}
}
return $users;
I remove some item from the collection and the
$users is an object of objects {{user1}, {user2}}
Removing an item from the collection seems to change the type of the variable $users
How could I remove some item from the collection while maintaining
the array of object?
EDIT
Here is the correct query
$users = User::whereHas('roles', function ($query) {
$query->whereBetween('level', [50, 999]);
})
->with('roles')
->where('company_id', '=', $idCompany)
->get();
return $users;
After manipulating the collection, you need to add ->all() to get the remaining collection;
return $users->all();
You could also do it this way
$filtered = $users->reject(function ($value, $key) {
return count($user->roles) == 0;
});
return $filtered->all();
As proposed by Alex in the comments, you could use whereHas() instead of with() to get directly all the users having a role between 50 and 999
$users = User::whereHas('roles', function ($query) {
$query->whereBetween('level', [50, 999]);
})
->where('company_id', '=', $idCompany)
->get();

Categories