Retrieve models that belongsToMany specific models - php

I've got a standard many-to-many relationship
class User {
public function roles() {
return $this->belongsToMany('Role');
}
}
class Role {
public function users() {
return $this->belongsToMany('User');
}
}
And it works very well.
But I need to select all the users that has exactly two specific roles.
$roleAdmin = Role::where('name', 'admin')->first();
$roleUser = Role::where('name', 'user')->first();
$users = //which users has BOTH $roleAdmin and $roleUser ??
Is it possible to achieve this using eloquent or I need a raw query?
PS the use-case is stupid, I know, it's just an abstraction of my real problem (that doesn't concern users and roles)

The best solution I found is to get admins and users and then use intersect() helper to get only users who are present both in $users and admins collections:
$users = User::whereHas('roles', function ($q) use($otherRoles) {
$q->where('name', 'user')->whereNotIn('name', $otherRoles);
})->get();
$admins = User::whereHas('roles', function ($q) use($otherRoles) {
$q->where('name', 'admin')->whereNotIn('name', $otherRoles);
})->get();
$result = $admins->intersect($users);
If you want to save some memory, you could pluck() only IDs, intersect() these and only then get users with whereIn().

This is not an eloquent solution(by all means)
$users = \DB::table('users')
->select(\DB::raw("GROUP_CONCAT(roles.name SEPARATOR '-') as `role_names`"), 'users.name')
->join('role_user', 'users.id', '=', 'role_user.user_id')
->join('roles', 'roles.id', '=', 'role_user.role_id')
->groupBy('users.id')
->having('role_names', '=', 'admin-user')
->get();
admin-user can be user-admin, which roles names comes first in the database. Please change table and column names as per your requirement

Related

How can I pick single record from one to many relation?

I have one to many relation based two tables users and games and there is also bridge table users_games (linking user_id to games).
I want to fetch a single record from games table based on provided game_id for specific user. I did some research and found whereHas() which is returning all games which are belongs to specific user. But I need to fetch one based on game_id. Can some one kindly let me know how can I fix issue in below script
$GameInfo = User::with('games')->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
Is this what you're trying to do?
$GameInfo = $request
->user()
->games()
->where('game_id', $request->game_id)
->first();
try this:
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])->whereHas('games', function ($query) use($request)
{
$query->where('game_id', '=', $request->game_id);
})->find(request()->user()->id);
If your relation 'games' is a hasMany() with table 'users_games', You can try this code
$GameInfo = User::with(['games' => function ($query) use($request)
{
$query->where('game_id', $request->game_id);
}])
->where('users.id', <user_id_variable>)
->first();
And the relation 'games' in User Model as
public function games()
{
return $this->hasMany('App\Models\UserGames', 'user_id', 'id');
}

Laravel : if record not found then search relational model

I am trying to find a record in User model , if the record doesn't exists in User model, Then find it in UserCompany model with relation name company in User model.
$companyUser = \App\User::whereHas('company', function ($query) use ($request) {
$query->where('company_email', '=', $request->email);
})->where('email', $request->email)->get();
I am getting empty set there, What am i missing.
You can do like this,
$companyUser = \App\User::where('email', $request->email)
->orWhereHas('company', function ($query) use ($request) {
$query->where('company_email', '=', $request->email);
})->get();

Laravel whereHas not working as expected

I have a relationship on my User table called 'roles'.
This relationship is:
public function roles()
{
return $this->hasMany('App\RoleUser');
}
I am only trying to fetch users that have a role of 3.
My query is:
User::where('name', $name)
->orWhere('email', $email)
->whereHas('roles', function ($q) {
$q->where('id', 3);
})->get();
This, however, is returning all users no matter what their role. I can confirm that all users have roles and the roles relationship is working.
The fields in the role_user table are 'id', 'user_id'
Looks like this issue was caused by a previous orWhere clause on the query.
Looking at the original query in the question:
User::where('name', $name)
->orWhere('email', $email)
->whereHas('roles', function ($q) {
$q->where('id', 3);
})->get();
This reads as where the name is 'name' and the user has roles and includes a role of 3. The query was also bringing in any results that had an email that matched my provided email but because it was 'orWhere' it ignored the other requirements.

How to fetch users not assigned to a particular role in Laravel [duplicate]

In Laravel we can setup relationships like so:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Allowing us to to get all items in a pivot table for a user:
Auth::user()->items();
However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.
Is there a simple way to do this?
Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of has)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
this works correctly for me!
For simple "Where not exists relationship", use this:
User::doesntHave('Role')->get();
Sorry, do not understand English. I used the google translator.
For simplicity and symmetry you could create a new method in the User model:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
To use call:
Auth::user()->availableItems();
It's not that simple but usually the most efficient way is to use a subquery.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
If this was something I did often I would add it as a scope method to the Item model.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Then use that later like this.
$items = Item::whereNotRelatedToUser($user_id)->get();
How about left join?
Assuming the tables are users, items and item_user find all items not associated with the user 123:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
this should work for you
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);
Ended up writing a scope for this like so:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
And then call:
Items::avail()->get();
Works for now, but a bit messy. Would like to see something with a keyword like not:
Auth::user()->itemsNot();
Basically Eloquent is running the above query anyway, except with a = instead of a <>.
Maybe you can use:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
Source: http://laravel.com/docs/4.2/queries#advanced-wheres
This code brings the items that have no relationship with the user.
$items = $this->item->whereDoesntHave('users')->get();

Paginate an eager loaded relationship

I'm using Laravel 3.x.
Post::with(
array('blogs' => function($query) {
$query->where('user_id', '=', Auth::user()->id);
} ))->get();
How can I paginate the posts?
From the feedback above it seems that eager loading isn't what you are looking for. Try this.
Post::where( 'user_id', '=', Auth::user()->id )->paginate( 10 );
Or you could add a posts method to the User model.
public function posts()
{
return $this->has_many( 'Post' );
}
Then use that to get the users posts.
Auth::user()->posts()->paginate( 10 );
FakeHeal,
Supposing that $user->has_one('blog'), you can simply use Laravel magic relationship getter:
$user = Auth::user();
$user->blog->posts();
If that's not the case and user->has_many('blog'), you need to use JOIN to retrieve all posts made to all of that user blogs.
$user = Auth::user();
$posts = Posts::join('blogs', 'blogs.id', '=', 'posts.blog_id')
->where('blogs.user_id', '=', $user->id)
->get('posts.*');
This is the solution I came up with. A user can have many blogs and a post can belong to many blogs.
/**
* Get all posts by a user for all blogs.
* #return paginated posts.
*/
public static function get_posts_for_user($user_id, $limit)
{
return Post::left_join('blog_post', 'posts.id', '=', 'blog_post.post_id')
->join('blogs', 'blogs.id', '=', 'blog_post.blog_id')
->where('blogs.user_id', '=', $user_id)
->paginate($limit);
}

Categories