Is there any way to return a parent model with a relationship but only return some of the relationship rows by using a where in?
That may be quite confusing, let me explain.
At the moment I have 2 models, Buildings and rooms, 1 building can have many rooms.
I want to be able to pass in an array of room ids, and return the sites and only the rooms that are in the array.
Heres what I have at the moment
if($request->input('ids') && !is_null($request->input('ids'))){
$ids = explode(',',$request->input('ids'));
//Exploded ids looks like this "2,4,11,55,56"
$buildings = Buildings::join('rooms')->whereIn('rooms.id',$ids)->get();
} else {
$buildings = Buildings::whereHas('rooms')->get();
}
At the moment this will return all buildings that have a room which id is in the ids array and all of its rooms, which ends up returning a building with 200+ rooms. I need it to return the building and ONLY the rooms that have an id in that array.
Is this possible?
I know I can do it the inverse way and get all rooms as the parent then get the buildings, but I need buildings to be the parent as i'm running a foreach like this with the results
foreach($buildings as $key => $building){
<h1>{{$building->name}}</h1>
foreach($building->rooms as $k => $room){
<p>{{$room->name}}</p>
}
}
Incase thats still confusing, the real world scenario is that i'm generating a PDF of rooms. The rooms can be selected by ticking a checkbox next to the room in a room list. I then need to be able to pass the array of room ids, and get all buildings that contain one of the rooms. Then get all of the rooms for each building where the room id is in the array.
First you need to know, whereHas only filter your parent result but not the eager loading relation. So you need to apply that filter in eager load too. Like this
$ids = explode(',',$request->input('ids'));
$buildings = Buildings::with(['rooms' => function($q) use ($ids) {
$q->whereIn('id', $ids);
}])->whereHas('rooms', function($q) use ($ids) {
$q->whereIn('id', $ids);
})->get();
Here whereHas filter buildings and using with filter rooms.
$ids = explode(',',$request->input('ids'));
$building_ids = Room::whereIn('id',$ids)->pluck('building_id');
$buildings_with_specific_rooms = Building::join('rooms', 'buildings.id', '=', 'rooms.building_id')->select('buildings.name', 'rooms.name')->whereIn('buildings.id', $building_ids)->whereIn('rooms.id', $ids)->get();
hope this helps you.
you can do this with the following code:
$ids = explode(',',$request->input('ids'));
$buildings = Buildings::whereHas('rooms', function($q) use ($ids) {
$q->whereIn('id', $ids);
})->get();
Hope this helps.
You can eager load the child relation and get the buildings out of the rooms collection:
$buildings = Room::with('building')
->with('building.room')
->whereIn('id', $ids)
->get()
->pluck('building');
For this to work you need to have the relation declared in both Building and Room models.
Related
I have a Prize, Ticket and User model. A prize can have many tickets, and a ticket can only be associated to one User.
Each Prize will have one Winning Ticket, what I am trying to do is list all my Users that have a winning Ticket like so:
$winning_tickets = Prize::WinnerSelected()->get('ticket_winner_id')->pluck('ticket_winner_id');
$users = User::with(['tickets' => function($query) use ($winning_tickets) {
$query->whereIn('id', $winning_tickets);
}])->get();
$winning_tickets returns an array of winning ticket ids, but the $users collection returns ALL my users, even users that have no ticket records.
Can anyone explain what I am doing wrong?
with() doesn't actually filter the User Collection being returned. To do that, you need to use whereHas():
$winningTickets = Prize::WinnerSelected()->get('ticket_winner_id')->pluck('ticket_winner_id');
$users = User::whereHas('tickets', function($query) use ($winningTickets) {
$query->whereIn('id', $winningTickets);
})->get();
Now, the $users Collection will only contain User records that have one or more Ticket records matching the given ticket_winner_id in $winning_tickets.
If you need to, you can use both with() and whereHas() to filter and eager load the associated Ticket records:
$winningTickets = Prize::WinnerSelected()->get('ticket_winner_id')->pluck('ticket_winner_id');
$filterClause = function ($query) use ($winningTickets) {
return $query->whereIn('id', $winningTickets);
};
$users = User::with(['tickets' => $filterClause])
->whereHas('tickets', $filterClause)
->get();
Define the the function ($query) as a reusable clause to avoid repetition, and voila!
Sidenote, you don't need to chain ->get() into ->pluck(); both Builder and Collection classes have a ->pluck() method, so this is valid:
$winningTickets = Prize::WinnerSelected()->pluck('ticket_winner_id');
I have a simple eloquent query and want to include another table with my results, however, the order of relationship results is incorrect.
Is it possible to order the results without using an SQLRAW statement
$groups = AttributeGroup::with('attribute')->where('page_id', $page->id)->get();
What I would like -
$groups = AttributeGroup::with('attribute')->orderBy('iteration', 'DESC')->where('page_id', $page->id)->get();
I get the error of Unknown column because this column is part of relationship table.
This will order each attribute relation of every attribute group result:
$groups = AttributeGroup::with(['attribute' => function ($query) {
$query->orderBy('iteration', 'DESC');
}])->where('page_id', $page->id)->get();
Is this what you want to achieve?
You can use closures to change the query when using with and has.
$groups = AttributeGroup::with(['attribute' => function($query){
$query->orderBy('iteration');
})->where('page_id', $page->id)->get();
Details are available on https://laravel.com/docs/5.6/eloquent-relationships#constraining-eager-loads
I am creating a search in laravel for an API but my search gives me the wrong results. I am trying to search by location and food type. I have the following tables:
foods
shops
shop_food
users
comments
Here is my search code:
public function searchShop($food, $location)
{
//
if($food == " " || $location == " "){
return $this->index();
}
//get all records where city and food are equal to
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->with(['foods'=> function($query) use($food){
$query->where('name','=', 'fish pepper'); }])
->get();
//check if empty and return all
if($shops->isEmpty()){
return $this->index();
}
return $shops;
}
my result is the below instead of just the record where location and food it shows all the shops filtered by location even where food isnt a match :
The with method that you're using does not filter in the way that you think it does. Your code actually filters the food results, telling Eloquent to retrieve all Shop and either no foods, or the foods with the name fish pepper. This is called constraining eager loads.
The method you're looking for is whereHas rather than with. This is referred to as querying a relationships existence.
$shops = Shop::where('city', '=', $location)
->with('comments.user')
->whereHas('foods', function($query) use($food){
$query->where('name','=', 'fish pepper');
})
->get();
This will now return only Shops that have a corresponding food entry with the name fish pepper.
If memory serves, whereHas won't actually populate foods for you, but in this instance you wouldn't need it, as it's safe to assume that they all have fish pepper. If you do want to pull all foods, change with('comments.user') to with(['comments.user', 'foods']).
Documentation for whereHas and other ways of achieving this can be found here.
Documentation about what you were doing with the with method can be found here.
Hope that helps.
Scenario
I have 3 main tables Employees,Jobs,Skills. Employees and Jobs has many-to-many relationship with Skills table.
So, an employee can have skills 1,2,3,5. A job may requires skills 1,3,5.
Now my question is how can I match the id's in an eloquent query. Like, if I want to search all the employees for a job requiring skills 1,3,5, it should search all the employees having all those skills(1,3,5)
You said you have an array of IDs, so use multiple whereHas():
$employees = Employee::JobLocations($jobZipId);
foreach ($skillIds as $id) {
$skilledEmployees = $emloyees->whereHas('skills', function ($q) use ($id) {
$q->where('id', $id);
});
}
$skilledEmployees = $skilledEmployees->get();
You can do it like, I am taking $job as already a defined object.
Employee::whereHas('skills', function($q) use($job) {
$q->whereIn('id', $job->skills->lists('id')->toArray());
})->get();
Update
In case of Laravel 5.3 and up, the method lists won't work (as its is removed) so you can also use pluck method instead of lists.
Hope this helps you to solve your problem.
I am wanting to query 3 seperate tables at once. The main one is Criteria, the two has Many tables, linked by criteria_id are Bedrooms and Properties.
A Criteria can have many Bedrooms and many Properties:
Criteria Model
public function bedrooms()
{
return $this->hasMany('Bedroom');
}
public function properties()
{
return $this->hasMany('Property');
}
I'm unsure if this is possible, but I want to query both of these tables, as well as Criteria to see which criteria has a certain bedroom and a certain property type. Please note, there could be multiple bedrooms and properties stored for each criteria_id.
So far, my query is:
$criterias = Criteria::select('id')
->where('min', '<=', Input::get('single_value'))
->lists('id');
My only logical explanation is -
Get all Criteria where Min <= Value and Criteria.Bedrooms = 1 and
Criteria.Properties = 5.
As if to loop through and see if a criteria has a bedroom / property with that value stored.
Many thanks for your help.
If you are looking for only criteria that meet your constraints, you can use whereHas() on the Bedroom and Property relationships. Then add your where() and lists().
$bedroom_id = '1';
$property_id = '5';
$criteria = Criteria::whereHas('bedrooms', function($q) use ($bedroom_id) {
$q->where('bedroom', $bedroom_id);
})->whereHas('properties', function($q) use ($property_id) {
$q->where('property', $property_id);
})->where('min', '<=', Input::get('single_value'))->lists('id');
Criteria.Bedrooms = 1 and Criteria.Properties = 5 was a little vague, but I added it in where they should go. I'm guessing you actually meant the id of 1 and 5 respectively so it would be getting all criteria that have both a bedroom of id 1 and a property of id 5.
Try Eager Loading the relationships
$criterias = Criteria::with('bedrooms', 'properties')
->where('min', '<=', Input::get('single_value'))
->lists('id');