Get relation for multiple objects with laravel - php

A User has many Phones. I want to get all the phones from active users. I can do:
$phones = [];
$users = User::with('phones')->where('active', 1)->get();
foreach($users as $user) {
$phones = array_merge($phones, $user->phones->toArray());
}
dd($phones); // <- Here are all the phones.
But I'm not sure it's the more elegant or laravel-ish way. Is there a built in magic function for such a case? Can I get all the phones from active users without writing a loop and insert in an Array?

You should use whereHas() method:
$phones = Phone::whereHas('user', function($q) {
$q->where('active', 1);
})->get();

You can also use whereExists, which can be more efficient if you're not using data from the users table:
Phone::whereExists(function($query) {
$query->select(\DB::raw('NULL'))
->from('users')
->whereRaw('users.id = phones.user_id AND users.active = 1')
})->get();

Related

How to retrieve users associated with multiple roles like ["role1","role2"...]

I have users and roles relationship as belongsToMany.
I now want to get all users have multiple roles by names.
I have tried as,
$role_ids = [1,2];
$users = User::whereHas('roles' , function ($query) use($role_ids) {
$query->whereIn('roles.id', $role_ids);
})->get();
But this approach gives me users who are associated with both or either one roles which is not i am expecting
You'll have to add multiple whereHas for that:
$role_ids = [1,2];
$users = User::with('roles');
foreach($role_ids as $role){
$users->whereHas('roles', function($q) use ($role){
$q->where('roles.id', $role);
});
}
$users = $users->get();

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();

Count from other table

I am having problem to count number of devices where guid is not null.
It need to get all the shops by user user_id and then count all the devices where guid is not null.
$shops = Shop::with('devices')->where('user_id', $userId)->get();
$deviceActive = $shops->reduce(function ($carry, $item) {
return $carry + $item->devices->whereNotNull('guid')->count();
});
dd($deviceActive );
It work when I do:
return $carry + $item->devices->count();
but it need to count where guid is not null.
I would also be interested to hear if there is alternative reduce approach.
Since $item->devices is a collection there is no whereNotNull() for collections. So try to use where():
$item->devices->where('guid', '<>', null)->count();
Try:
$shops = Shop::with('devices')
->where('user_id', $userId)
->where('guid', '!=', null)->get();
$get_count = count($shops); // it return how many values have $shops
OR
$shops= DB::table('devices')->where('user_id', $userId)
->where('guid', '!=', null)->get();
$get_count = count($shops);
if you did not have the class DB add at your controller:
use DB;

Laravel Eloquent: ordering Many-to-Many query- user favorites first

My Laravel app returns a list of ships for users to manage. I want to return ships that are "favorited" by the active user before the rest of the ships. eg:
Best ship (*)
Ship I manage often (*)
Another ship
Beta ship
Cornelia
I have a many-to-many relationship between User and Ship, where ship->userFavorite returns the users who have "favorited" the ship.
I can imagine 2 solutions, but I'm not sure if they are possible and how to implement them if they are.
An orderby query that sorts based on favorite being equal to active user
$ships = Ship::orderby([ship->userfavorite being equal to Auth::User()], 'ASC')
->orderby('name', 'ASC')->get();
My current solution: I use a wherehas query to return the favorited ships first ($favships), and use another query to return all the ships ($ships). For this solution I would like to remove the favoried ships from the second query. But how can I elegantly remove these ships from the results?
$user = Auth::user();
$favships = Ship::wherehas('userFavorite', function($q) use($user)
{
$q->where('id', $user->id);
})->orderBy('name', 'ASC')->get();
$ships = Ship::orderBy('name', 'ASC')->get();
Any help to increase my understanding of this problem would be greatly appreciated!
you can use
$normalShips= $ships->diff($favships);
But I think you can reduce from 2 queries to 1 query:
//Ship.php
public function currentUserFavorite() {
$this->userFavorite()->where('id', Auth::user()->id);
}
// get all ships
$ships = Ship::with('currentUserFavorite')->orderBy('name', 'ASC')->get();
// finally
$favships = $ships->where('currentUserFavorite.0.id', Auth::user()->id);
$normalShips = $ships->diff($favships);
// or do a loop
foreach ($ships as $ship) {
if ($ship->currentUserFavorite->count() > 0) {
$favships[] = $ship;
} else {
$normalShips[] = $ship;
}
}

Exclude items from a collection in Laravel

In my Laravel app, I have the concept of users, friends, and groups. A user can create and sort friends into groups.
A user can have many friends:
function friendsOfMine()
{
return $this->belongsToMany('App\User', 'helpers', 'user_id', 'helper_id')
->wherePivot('accepted', '=', 1);
}
A group can have many users:
public function groupMembers()
{
return $this->belongstoMany('App\User')->withTimestamps();
}
In the UI for adding friends to a group, I want to show the full list of a user's friends, but exclude those friends that have already been added to the group. My Groups Controller function looks like this, though I'm positive I'm off-base.
public function add($id)
{
$group = Group::findOrFail($id);
$helpers = $group->groupMembers;
$id = $helpers->lists('id');
$invitees = $this->user
->friendsOfMine()
->where('helper_id', '!=', $id)
->Paginate(5);
return view('groups.add', compact('group','helpers','invitees'));
}
Ideally, what I'd love is some way to write:
$helper = $group->friendsOfMine->not->groupMembers->Paginate(5);
Is there anyway to filter data using functions from two different models?
With your approach you would have to use whereNotIn() since you have an array of ids:
$id = $helpers->lists('id');
$invitees = $this->user
->friendsOfMine()
->whereNotIn('helper_id', $id)
->Paginate(5);
However you probably could something like this as well (assuming the relation group)
$invitees = $this->user
->friendsOfMine()
->whereDoesntHave('group', function($q) use ($id){
$q->where('group_id', $id); // Note that id is the group id (not the array of ids from the helper)
})
->Paginate(5);
For a nicer syntax (like in your example) you should look into Query Scopes

Categories