Match array of ids from pivot table - php

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.

Related

Laravel / Eloquent: nested WhereHas

I've just started learning Laravel and stumbled upon one issue which I can't make work using the Eloquent relationships.
Let's assume that I have a Worker model, a Skill model and a pivot table skills_workers to keep the many-to-many relationship.
When I'm trying to get all the workers, who have following skill, then it goes without a problem using the following syntax:
$skill='carpenter';
$workers=Worker::whereHas('skills', function (Builder $query) use($skill){
$query->where('name','=',$skill);
})->get()();
The problem begins when I need to find all workers who have the set of skills. For example, carpenter-driver-chef (just for example). If the worker should have one of the skills, then I'd just use the whereIn function, but I need to find the worker who posess all of the skills in array.
I can't make the nested WhereHas as every time the user performs a search the skill set might be different. Sometimes it's just 1 skill, sometimes 2 and so on.
So the following construction:
$skills=['carpenter','driver'];
$workers=Worker::whereHas('skills', function (Builder $query) use($skills){
$query->where('name','=',$skills[0]);
})->whereHas('skills', function (Builder $query) use($skills){
$query->where('name','=',$skills[1]);
})->get();
is not an option.
Is there a way to use whereHas inside of a foreach loop, for example? Or, maybe, there is a more elegant way of performing such queries? None of the other posts on StackOverflow that I've found, helped...
I'd really like to avoid using the raw SQL queries, if possible.
Thank you in advance
As your $skills variable appears to be an array, you could use the Eloquent whereIn function.
$workers = Worker::whereHas('skills', function (Builder $query) use ($skills) {
$query->whereIn('name', $skills);
})->get();
Update
The following should get you a collection of Workers that have all the Skills.
$workers = Worker::whereHas('skills');
foreach ($skills as $skill) {
$workers->whereHas('skills', function (Builder $query) use ($skill) {
$query->where('name', $skill);
})->get();
}
$workers->get();
I think you can use foreach for skills to get multiple matching condition
$workers=Worker::whereHas('skills', function (Builder $query) use($skills){
foreach($skills as $value){
$query->where('name',$value);
}
})->get();
You can start with getting all skills and after that you can use whereIn like this
$skills=['carpenter','driver'];
$skills_id = Skill::whereIn(['name',$skills])->pluck('id');
By using pluck the query will return an array of IDs [1,3,...] not model.
$workers = Worker::whereHas('skills', function(Builder $query) use ($skills_id) {
$query->whereIn('id', $skills_id);
})->get();

Query Laravel Eloquent many to many where all id's are equal

I making a project based on Laravel and have the tables: companies, attributes, and attribute_company related as Many To Many relation when attribute_company use as a pivot table to connect companies and attributes tables.
I get an array of attribute_id's from the client and I need to get results of the companies that has the whole attributes exactly.
The only solution I found is to query whereHas combined with whereIn inside like this:
Company::whereHas('attributes', function (Builder $query) use ($atts_ids) {
$query->whereIn('attribute_id', $atts_ids);
})->get();
This query will return companies if at least one attribute_id found (which is not what I am looking for).
It would be great if anybody can make it clearer for me.
Thank you all in advance :)
One possible solution:
$company = new Company();
$company = $company->newQuery();
foreach($atts_ids as $att_id)
{
$company = $company->whereHas('attributes', function (Builder $query) use ($att_id) {
$query->where('attribute_id', $att_id);
});
}
$company = $company->get();

Laravel Where In on Where Has

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.

How to use a where query inside a laravel relationship?

Hello i have a CommentRetours table which connects the retours to the comments.
The DB:
I need to display all comments for a user and return that to the view.
I now have this query:
$comments = CommentRetours::with(['comments' => function($q) {
$q->where('user_id',Auth()->user()->id);
}])->get();
This return NULL..
The user_id is inside the comments table.
As constructure example i will add this:
What am i doing wrong?
The question is quite old but the solution adopted by OP being less than ideal, here is what he probably should have done:
$comments = CommentRetours::whereHas(['comments' => function($q) {
$q->where('user_id', Auth::id());
}])->with('comments')->get();
whereHas checks if the CommentRetours have some comments that match the condition.
I also replaced Auth()->user()->id by Auth::id().
I fixed it by adding user_id to the CommentRetours table, now I wont have to query inside the other relationship.
I can just see user_id directly inside the table.

Complex joins in Eloquent

I have a table of itineraries. An itinerary belongs to a customer and has multiple days. A package is assigned to each of these days. I want to be able to produce a manifest showing which customers are allocated to a package and on which days.
I'm struggling with Eloquent, because you can't do queries beyond a one-to-Many relationship
What i want to do is this:
return $this->package->where('PackageID, $id)->itineraryDay->itinerary->customer->select('CustomerID', 'Date')
But can only really achieve it using the query builder:
return DB::connection($this->connection)
->table('t_package as PA')
->join('t_itinerary_day_map as IDM', 'IDM.PackageID', '=', 'PA.PackageID')
->join('t_itinerary_day as ID', 'IDM.ItineraryDayID', '=', 'ID.ItineraryDayID')
->join('t_itinerary as IT', 'IT.ItineraryID', '=', 'ID.ItineraryID')
->join('t_customer as CC', 'CC.ItineraryID', '=', 'IT.ItineraryID')
->where('PA.PackageID', $id)
->select('CC.CustomerID', 'ID.Date')
->distinct()
->get();
I really want to use Eloquent as I hate hardcoding table names and i've already created relationships for these models, but can't see any way around it
I believe you could do something like this to find customers that have a package with the given ID:
$packageId = 42;
$customers = $customer->whereHas('packages', function($q) use($packageId){
return $q->where('package_id', $packageId);
})->get();
How would that work for what you want?
I'll have to make few assumptions on your relationship but it seems doable.
If one ItineraryDay belongs to one Itinerary. And one Itinerary belongs to one Customer. And one ItineraryDay may have more than one Package.
$packageID = 111;
$itineraryDays = ItineraryDay::with('itinerary.customer')
->whereHas('package', function($q) use($packageID) {
$q->where('PackageID', $packageID);
})
->get();
foreach($itineraryDays as $itineraryDay) {
var_dump($itineraryDay);
var_dump($itineraryDay->itinerary->customer);
}
I'm not sure if i get your relationship method naming correct, but hopefully this works.

Categories