laravel nested hasmany with where clause on last child - laravel

I want to get data from nested relationships with where clause on the last child, (as json nested response). I'm making an API with 4 nested models with hasmany relations like this:
modelA ->hasmany modelB ->hasmany modelC ->hasmany modelD
(modelA is first parent etc..)
I have a route on api.php
Route::get('/rdata/{string}', [ModelAController::class, 'getdata']);
On my ModelAController:
public function getdata($string)
{
return ModelA::thedata($string);
}
And then on my ModelA
public static function thedata($string)
{
return ...
}
I want to get data based on a field of the 4th ModelD. How can I do a where clause on the 4th child maybe something like this:
where('column', 'like', '%'.$string.'%')
I also tried with collection resources, I can get all the data nested fine but I cant perform the query I want on the last child. Best thing I've achieved is doing the query on the last child load parents but it doesnt work properly
return ModelD::where('column', 'like', '%'.$string.'%')->with('modelc.modelb.modela')->get();
or even with load
return ModelD::where('column', 'like', '%'.$string.'%')->get()->load('modelc.modelb.modela');
then on ModelC:
public function modelb()
{
return $this->belongsTo(ModelB::class, 'f_key','f_key');
}
and the same for the other parents but this is wrong as I get all the children inside them again and also the json is inversed. I want to keep the format:
$response = ['ModelA' => [
'id' => '..'
'field1' => '..'
'ModelB'=>[
etc..
]]
Or is there another way to do it that I don't see? In a way I need something like this but formated in nested json
return DB::table('modela')
->join('modelb', 'modela.id', 'modelb.modela_id')
->join('modelc', 'modelb.id', 'modelc.modelb_id')
->join('modeld', 'modelc.id', 'modeld.modeld_id')
->where('column', 'like', '%'.$string.'%')
->get();
Edit1: I got this so far based on the answer and I want to get parent columns on last child
return self::with('modelb.modelc.modeld')-> whereHas('modelb.modelc.modeld', function ($modeld) use ($string) {
$modeld->where('column', 'like', "%$string%");
->whereColumn('modelc.column2', 'modeld.column2')
->whereColumn('modelc.column3', 'modeld.column3');
})
->with(['modelb.modelc.modeld' => function ($modeld) use ($string) {
$modeld->where('column', 'like', "%$string%");
->whereColumn('modelc.column2', 'modeld.column2')
->whereColumn('modelc.column3', 'modeld.column3');
}])
->get();

I think you can do that with whereHas/with Closures.
ModelA::query()
->whereHas('modelb.modelc.modeld', function ($modeld) use ($string) {
$modeld->where('column', 'like', "%$string%");
->with(['modelb.modelc.modeld' => function ($modeld) use ($string) {
$modeld->where('column', 'like', "%$string%");
}])
->get();
Something like this, but probably more refined in the conditions.
I think based on your edit, that this query should work for your needs.
ModelA::query()
->whereHas('modelb.modelc', function ($modelC) use ($string) {
$modelC->whereHas('modeld', function ($modelD) use ($string) {
$modelD->whereColumn('table_c.column2', 'table_d.column2')
->whereColumn('table_c.column3', 'table_d.column3')
->where('table_d.column', 'like', "%$string%");
});
->with(['modelb.modelc' => function ($modelC) use ($string) {
$modelC->with(['modeld' => function ($modelD) use ($string) {
$modelD->whereColumn('table_c.column2', 'table_d.column2')
->whereColumn('table_c.column3', 'table_d.column3')
->where('table_d.column', 'like', "%$string%");
}]);
}])
->get();

Related

How to make laravel query with search condition and filter condition from relational table

I have models 'Case' and 'Type' in many to many relation. 'Case' belongs to a 'Type'. Now I am trying to implement search and filter in my case index. I have already included the search in the query builder so that the keywords match multiple columns and also columns in related table. Now I am trying to incorportae a filter also. But when I am including that filter in query builder, it is not working. Including either search or filter is working but including both together is not.
Here is my code to include the search in query builder:
$key = $this->search;
$cases = Landcase::query()->orderBy('created_at', 'DESC');
$cases
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
This much is perfectly working. I can search in title, number and type's name. Now here is the additional code to include the filter with type id(s) so that only specific type's cases will be shown and the search will work on those only.
if($this->selected_types){
$ids= $this->selected_types;
$cases->whereHas('type', function ($query) use ($ids){
$query->whereIn('id', $ids);
});
}
this block is not affecting the collection. But if I comment out the first block, this works. How can I incorporate it to my existing query builder block?
You have several OR in your where clauses so adding the last part makes it something like:
WHERE … OR … OR (… AND …)
What you need is to group your orWheres:
$cases->where(function ($query) {
$query
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
});
More here: https://laravel.com/docs/9.x/queries#or-where-clauses

how to use whereHas in laravel

I am new to laravel,
I need to create a query for the db,
$query = Deal::query();
I want to use the wherehas operator.
this is my code that is worked.
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus);
// \Log::info('The request precedence: '.$precedenceStatus);
}
I want to add this code also to the query
if($person) {
$query->whereHas('personnel', function ($subQuery) use ($person) {
$subQuery->where('id', '=', $person);
});
}
So I need to change the first code?
how I can convert the first code to wherehas?
the first code is from table called deal, the second section is from realtionship called personnel.
the second section worked in other places in the code, I just need to fix the first section and not understand what to write in the use
I try this and get error on the last }
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus)
-> when ($person, function($query) use($person) {
$query->whereHas('personnel', function ($query) use ($person) {
$query->where('id', '=', $person);
});
})
}
There is a method you can use called when(, so that you can have cleaner code. The first parameter if true will execute your conditional statement.
https://laravel.com/docs/9.x/queries#conditional-clauses
$result = $query
->where('precedence', '=', $precedenceStatus)
->when($person, function ($query) use ($person) {
$query->whereHas('personnel', fn ($q) => $q->where('id', '=', $person));
})
->get();
You should also be able to clean up your precedence code prior to that using when( to make the entire thing a bit cleaner.
Querying to DB is so easy in laravel you just need to what you want what query you want execute after that you just have to replace it with laravel helpers.Or you can write the raw query if you cant understand which function to use.
using,DB::raw('write your sql query').
Now Most of the time whereHad is used to filter the data of the particular model.
Prefer this link,[Laravel official doc for queries][1] like if you have 1 to m relation ship so u can retrive many object from one part or one part from many object.like i want to filter many comments done by a user,then i will right like this.
$comments = Comment::whereHas('user', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
$comments = Here will be the model which you want to retrive::whereHas('relationship name', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
you can also write whereHas inside whereHas.
[1]: https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence

Relational Query In laravel

My Code
$videos = ExamVideo::with([
'user' => function ($query) use ($request) {
$query->where('user_name', 'like', '%'.$request->search.'%');
}
])->where('is_marked','=', 0)->get();
return $videos;
I want to get only those videos where is_marked is zero & it's relation user_name match my search result.
But I get all the videos which marked is zero.
The with method is only for eager loading and not used to filter your records.
You can accomplish what you want with the whereHas method which will perform a query on the relation:
$videos = ExamVideo::whereHas('user', function ($query) use ($request) {
$query->where('user_name', 'like', "%{$request->search}%");
})->where('is_marked', false)->get();
For more information about whereHas: https://laravel.com/docs/6.x/eloquent-relationships#querying-relationship-existence

how to Ignore "where clause" if no query is provided with Laravel

I want to get all the leads when the query is empty, but if the 'subService' query is provided then it should fetch all the leads that have the same subService name
I am trying to get sub service name by providing this subService.name in where clause
subService is one many relationship
this is not the full code.
return Lead::with(['subService'])
->where(function ($query) use($subService) {
if ($subService) {
$query->where('subService.name','like', '%'.$subService.'%');
}
})
return Lead::with(['subService'])->when($subservice, function ($query) use ($subservice) {
return $query->whereHas('subService', function ($query) use ($subservice) {
$query->where('name', 'LIKE', "%{$subservice}%");
});
})->get();

laravel - send a selected field to with function

how can i use "airpot_id" in with function?
City::select([
"cities.id",
"airports.id As airport_id"
])
->RightJoin('airports', function ($join)
{
$join->on('cities.id', '=', 'airports.city_id');
})
->with(["airports" => function ($q)
{
$q->where("id",airport_id) //I want to use here.
}])
->paginate(env("PER_PAGE", 10))->toArray();
Why are you using joints like this? Use the eloquent query builder. Maybe you should post you modal, relations and your objective. In general, if we need to pass value inside function we use use($variable). Something like this
$airport_id = 1;
$data = City::with(["airports" => function ($q) use($airport_id){ //Like this we pass data
$q->where("id",airport_id)
}])->get();
I don't think this will work in your case

Resources