Laravel eloquent collection query - laravel

I am trying to create a search endpoint for one of my pages but i am having a little trouble writing the query for it to get it going
Team Model
public function members()
{
return $this->hasManyThrough('App\User', 'App\Membership', 'team_id', 'id', 'id', 'user_id');
}
**Member(user) Model **
public function skills()
{
return $this->belongsToMany('App\Skill')->withPivot('boosted');
}
if i access the collection via $team->members; it returns the correct team members
Illuminate\Database\Eloquent\Collection {#1507 ▼
#items: array:2 [▼
0 => App\User {#1508 ▶}
1 => App\User {#1509 ▶}
]
}
i have attempted below within my search function but when i dump the query it basically just returns all the team's and every member belonging to them when it should only be returning 2 members that belong to one specific team like the eloquent collection above.
public function search(Team $team, Request $request)
{
$search = $request->get('search');
$members = Team::with('members')->whereHas('members', function ($q) use ($search) {
$q->where('id', 'like', "%$search%");
})->whereHas('members.skills', function ($q) use ($search) {
$q->where('name', 'like', "%$search%");
})->paginate(10);
return view('team.show', compact('team', 'members'));
}
Are you able to do where() directly on the collection like this?
$members = $team->members;
$memberQuery = $members->where('name', 'like', "%$search%")
->orWhere('id', 'like', "%$search%");
the idea is to go through the member relation within the team model then add the abilty to search through specific fields and do a WhereHas on the skills relation within the member(user model) so i can also search for a field within that too.
I am a little lost on the solution to my problem can anyone point me in the right direction?

$members = Team::whereHas('members', function ($q) use ($search) {
$q->where('id', 'like', "%$search%")
->orWhere(function($q) use($search){
$q->whereHas('skills', function($q) use($search){
$q->where('name', 'like', "%$search%");
});
});
})
->with([
'members' => function ($q) use ($search) {
$q->where('id', 'like', "%$search%")
->orWhere(function($q) use($search){
$q->whereHas('skills', function($q) use($search){
$q->where('name', 'like', "%$search%");
});
})
},
'memebers.skills' => function($q) use($search){
$q->where('name', 'like', "%$search%");
}
])
->paginate(10);
whereHas plays the main role, as it will get only those teams who have members, whose id is like $search or their skills have name like $search.
And as we only need to get the members which satisfy the condition, it is being handled in the eager loading(we're eager loading only those particular members whose id is like $search or their skills have name like $search.)
You can refer to this blog. It has explained this kind of approach in detail
https://medium.com/#sander3/laravel-nested-eager-loading-contraints-9742c2dac4b9

Related

Conditionals on multiple Laravel relationships

I got table called Vehicles in whose model Vehicle are defined two relationships.
public function type(){
return $this->belongsTo("App\VehiclePivot", 'vehicle_type_id', "id");
}
and
public function workOrganization(){
return $this->belongsTo("App\WorkOrganization", 'workOrganization_id', "id");
}
When used as
$vehicles = Vehicle::with("type", "workOrganization");
they work great. Show for every vehicle their type and their work organization.
If i try
$vehicles = Vehicle::whereHas("type", function($query) use($request){
$query->where('name', '=', $request->type);
})->with("type")->get();
or
$vehicles = Vehicle::whereHas("workOrganization", function($query) use($request){
$query->where('name', '=', $request->workOrg);
})->with("workOrganization")->get();
They also work.
User can enter type or workOrganization, or both or neither of them.
If one is present, then only that relationship should "execute", if both, then both relationship should, also if it neither of them, then also both should execute.
Can it be done via eloquent?
I think you can use when
$vehicles = Vehicle::when(!empty($request->workOrg),function ($query) use($request){
$query->whereHas("workOrganization", function($query) use($request){
$query->where('name', '=', $request->workOrg);
});
})->when(!empty($request->type),function ($query) use($request){
$query->whereHas("type", function($query) use($request){
$query->where('name', '=', $request->type);
});
})
->with("workOrganization","type")
->get();

Laravel Query Through Relationship

I am creating a search in laravel where customers can search for vehicles.
Table 1
Vehicle
VIN
PLATE
make_and_model_id
Table 2
Vehicle Makes and Models
id
make
model
Relationship in Table 1: Vehicle
public function vehicle_make_and_model_fk()
{
return $this->belongsTo('App\Models\VehicleMakeAndModel', 'vehicle_make_and_model_id');
}
So I am searching for VIN or Plate. That works fine.
I also am Searching for a Make and Model name which is a foreign key.
I pulled in the related table using with which works fine.
Now how to search through the columns of Make and Model Table?
if($request->ajax()) {
$search = $request->search_query;
$vehicles = Vehicle::with('vehicle_make_and_model_fk')
->where(function ($query) use ($search) {
$query->where('plate', 'LIKE', '%'.$search.'%')
->orWhere('vin', 'LIKE', '%'.$search.'%')
->orWhere('vehicle_make_and_models.make', 'LIKE', '%'.$search.'%');
})
->limit(5)
->get();
echo json_encode($vehicles);
exit;
}
To filter the relationship, you need to use a closure in your with()
For example:
$vehicles = Vehicle::query()
->with([
'vehicle_make_and_model_fk' => function ($query) use ($search) {
$query->where('make', 'like', "%$search%")
->orWhere('model', 'like', "%$search%");
}
])
->where(function ($query) use ($search) {
$query->where('plate', 'like', "%$search%")
->orWhere('vin', 'like', "%$search%");
})
->limit(5)
->get();
Eloquent Relationships - Constraining Eager Loads
$vehicles = Vehicle::where('plate','LIKE','%'.$search.'%')
->orWhere('vin','LIKE','%'.$search.'%')
->with('vehicle_make_and_model_fk')
->orwhereHas('vehicle_make_and_model_fk',
function($query) use($search){
$query
->where('vehicle_make_and_models.make','like',$search.'%')
->orWhere('vehicle_make_and_models.model','LIKE','%'.$search.'%');
}
)
->limit(5)
->get();
This query worked. It would search and bring results from vehicle table and would go to makes and models table and bring results from there as well. Based on - Laravel Eloquent search inside related table

Laravel with where like condition in relationship

I have a WorkPackage model with below relationship.
public function work_items()
{
return $this->hasMany(WorkItem::class);
}
And WorkPackage has 'title','project_id' columns and work_items has 'title','work_package_id' columns. Now I want to search user typed keyword is matching with title in both table.
Here is my tried function
public function getWorkPackageProposals($projectId, $title)
{
$result['data'] = $this->workPackage->with(['work_items' => function ($query) {
$query->where('title', 'LIKE', "%{$title}%");
}])
->where('project_id', $projectId)
->where('title', 'LIKE', "%{$title}%")
->get();
return $result;
}
but its not working.
Please find the below code for I used to create object of WorkPackage.
public function __construct(WorkPackage $workPackage)
{
$this->workPackage = $workPackage;
$this->setModel(WorkPackage::class);
$this->workItemRepository = app(WorkItemRepository::class);
}
whereHas() works to specify additional filters for the related model to check :
$result['data'] = $this->workPackage->whereHas('work_items', function ($query) use ($title) {
$query->where('title', 'LIKE', "%{$title}%");
})
->where('project_id', $projectId)
->where('title', 'LIKE', "%{$title}%")
->get();
return $result;
Below code works for me. Thanks to everyone for help to get right answer.
$result['data'] = $this->workPackage->with(['work_items' => function ($q) use ($title) {
$q->where('title', 'LIKE', "%{$title}%");
}])
->where('project_id', $projectId)
->where('title', 'LIKE', "%{$title}%")
->get();
return $result;
I think your problem will be solved with join method:
$result = $this->workPackage
->join('work_items', 'work_packages.id', '=', 'work_items.package_id')
->where('work_items.title', 'LIKE', '%{$term}%')
->orWhere('work_packages.title', 'LIKE', '%{$term}%')
->get();

Laravel 5.8: orWhere anulls the whereHas effect

I'm trying to do a simple search function. For that, I implemented the following query:
Profile::whereHas('accounts', function ($query) use ($id) {
$query->where('user_id', $id);
})
->where('username', 'LIKE', "%{$search}%")
->paginate(20);
The whereHas selects the profiles that the user with $id has.
Then, with the where we figure out if the search match with some username in the database.
Finally, we paginate the results.
All right so far.
The problem comes when I add a orWhere like that:
Profile::whereHas('accounts', function ($query) use ($id) {
$query->where('user_id', $id);
})
->where('username', 'LIKE', "%{$search}%")
->orWhere('fullname', 'LIKE', "%{$search}%")
->paginate(20);
I want that the query analyses if the search matchs with the username and/or the fullname. When I add the orWhere works, but the whereHas doesn't do its work and the results are from all the users.
You need to do it like this:
Profile::whereHas('accounts', function ($query) use ($id) {
$query->where('user_id', $id);
})
->where(function($query) use ($search) {
$query->where('username', 'LIKE', "%{$search}%")
->orWhere('fullname', 'LIKE', "%{$search}%");
})
->paginate(20);

Constrain which relationship models are returned on whereHas

An example from the documentation:
$posts = App\Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
This will look for posts that has at least one comment with foo. It will return the post model with all comments. Is there a way to restrict this to return post model and related comments that only have foo?
I'm aware we can loop through $posts with a conditional later on but I'm looking for a solution via the query builder if possible.
From the docs:
https://laravel.com/docs/5.7/eloquent-relationships#constraining-eager-loads
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
Edit, the other answer is correct.. you should add it to you actual query, not just replace it as i suggested first:
So, from your example it would look like this:
$posts = App\Post::with('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
You could also just add a lazy loading after your query
$posts->load(['comments' => function ($query) {
$query->where('content', 'like', 'foo%');
}]);
Edit, solution suggested by #HelenChe (original asker), it's basically the same, useful if with and wherehas are going to have the same filter function.
$posts = Post::whereHas('comments', $filter = function ($query) {
$query->where('content', 'like', 'foo%');
})->with(['comments' => $filter])->get();
If you want only the posts that has at least one comment with foo with those comments. you have to combine these two function, whereHas() and with()
$posts = App\Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
->with('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();

Resources