Conditional wherehas with Laravel Eloquent - php

I have one similar (area) value in two tables one and two and these two tables has relation with main table master. At a time, the master table will be having data in one relation only and the other one will be null.
With this architecture, I have a search page where user can search any values related to these tables and the search fields are placed with AND condition.
Here, if user enters some value for area I need to check the area value exists in any one of the tables (one or two) without breaking the AND condition. Tried the below code but it is breaking AND rule and considering OR. Any suggestions to fix?
$result = Master::where(function ($query) use ($request) {
if ($request->filter == true) {
$query->where('user_id', Auth::user()->id);
}
// other conditions here
if (!empty($request->area_from) && !empty($request->area_to)) {
$query->whereHas('one', function ($query) use ($request) {
$query->whereBetween('area', [$request->area_from, $request->area_to]);
});
$query->orWhereHas('two', function ($query) use ($request) {
$query->whereBetween('area', [$request->area_from, $request->area_to]);
});
}
// other conditions here
})->with(['one', 'two'])->paginate($request->item);

You are wrapping all of your where statements in brackets. I think what you want to do is pull your first part of the query out of the where clause so that you can easily wrap the whereHas part in brackets.
// Initialise the model
$query = new Master;
// Start building the query
if ($request->filter == true) {
$query->where('user_id', Auth::user()->id);
}
if (!empty($request->area_from) && !empty($request->area_to)) {
// Wrap these in brackets so we don't interfare with the previous where
$query->where(function($query2) use ($request) {
$query2->whereHas('one', function ($query3) use ($request) {
$query3->whereBetween('area', [$request->area_from, $request->area_to]);
});
$query2->orWhereHas('two', function ($query3) use ($request) {
$query3->whereBetween('area', [$request->area_from, $request->area_to]);
});
}
}
$query->with(['one', 'two'])->paginate($request->item);

You can create a merged relationship refer this link
public function mergedOneAndTwo($value)
{
// There two calls return collections
// as defined in relations.
$onedata= $this->one;
$twodata= $this->two;
// Merge collections and return single collection.
return $onedata->merge($twodata);
}
and use whereHas('mergedOneAndTwo')

Using a closer where and making the conditional inside may work fine
$master = Master::with('one')->with('two');
$result = $master->where(function($subQuery)
{
$subQuery->whereHas('one', function ( $query ) {
$query->whereBetween('area', [$request->area_from, $request->area_to] ); //assuming $request->area_from, $request->area_to is range of value
})
->orWhereHas('two', function ( $query ) {
$query->whereBetween('area', [$request->area_from, $request->area_to] );
});
});

Related

Laravel eloquent query not working with sub query

so we have a page where we have to filter a list of students based on filters selected by the user in the front end.
Filters in the front end
Filter by Subjects (students who have opted for subjects)
Filter by location (students who are part of a location)
Filter by Gender (self explanatory)
Now Location & Gender are part of student table, so it is very easy to use these filters with simple query but subjects is a totally different table
Check the attached table
Query current we have
$student = Student::select('*')
->where(function ($query) use ($request) {
if (!empty($request->location)) {
$query->whereIn('location', $request->location);
} else if (!empty($request->gender)) {
$query->where('gender', $request->gender);
} else if (!empty($request->subjects)) {
// selecting students by id who enrolled for a particular subject
// end user can select multiple subjects
$query->whereIn('id', function($subjectQuery) use ($request) {
$subjectQuery->select('student_id')
->whereIn('subject_id', [$request->subjects])
->from('student_subjects')
->get();
});
})->get();
when passing {"subject": [201, 205]}
getting following error
Nested arrays may not be passed to whereIn method
But when passing {"subject": [201]}
This return empty result,
What are we doing wrong? or what we can do to improve this query?
In this case I usually use when() and whereHas() (assuming you have model StudentSubject and have the relationship). It will look like this:
$student = Student::select('*')
->when(!empty($request->location), function ($query) use($request) {
$query->whereIn('location', $request->location);
})
->when(!empty($request->gender), function ($query) use($request) {
$query->whereIn('gender', $request->gender);
})
->when(!empty($request->subjects), function ($query) use($request){
$query->whereHas('StudentSubject',function($sub) use($request) {
return $sub->whereIn('subject_id',$request->subjects);
});
})
->get();
I don't know your $request->subjects format so I assuming it's just the usual array like [1,2,3].

How can we use when clause for multiple tables in laravel eloquent query?

I'm using condition based queries but i have a problem how can i use (when) with other table/model because i have to put condition based on the other table column (branch_id).
branch_id comes from steps table.
ContactBoard::with(['steps'])
->when($request->has('branch'), function ($query) use ($request) {
$query->where('steps.branch_id', $request->query('branch'));
})
->where('id', $board_id)->get()->first();
You can make it like
$query->when($request->has('branch'), function ($q)use($request) {
return $q->with('steps',function($q){
$q->where('branch_id', $request->query('branch'));
});
});
$query = $query->get();

Laravel - whereHas with where affect other rows

I have an application where I want to fetch parent records based on children conditionals. Current problem is that I have Students, where they have multiple study fields and study fields belong to one faculty. Pivot table students_study_fields has attribute study_status_id.
What I need is, for example, fetch all students and their study fields which belongs to "prf" faculty AND pivot has study_status_id = 1.
So I write a query like this.
return Student::with(['studyfields' => function ($query1) use ($studyStatusId, $facultyAbbreviation) {
$query1->whereHas('pivot', function ($query2) use ($studyStatusId, $facultyAbbreviation) {
$query2->where('study_status_id', $studyStatusId);
});
$query1->whereHas('studyprogram', function ($query4) use ($facultyAbbreviation) {
$query4->whereHas('faculty', function ($query5) use ($facultyAbbreviation) {
$query5->where('abbreviation', $facultyAbbreviation);
});
});
}])->get();
But this query fetch students witch study_status_id = 2 as well because exists record where this same study field (its code) has relation with student, where study_status_id = 1.
So I don't want to include this studyfield if somewhere exists record with status = 1 in pivot but only if has status = 1 for current row
You need to chain the queries...
return Student::with(['studyfields' => function ($query1) use ($studyStatusId, $facultyAbbreviation) {
$query1->whereHas('pivot', function ($query2) use ($studyStatusId, $facultyAbbreviation) {
$query2->where('study_status_id', $studyStatusId);
})->whereHas('studyprogram', function ($query4) use ($facultyAbbreviation) {
$query4->whereHas('faculty', function ($query5) use ($facultyAbbreviation) {
$query5->where('abbreviation', $facultyAbbreviation);
});
});
}])->get();
Otherwise it will re-start the query1 so you won't get AND kind of query, only get the second part
Side Note: However, I want to warn you that whereHas is a slow query if you have many rows as it goes through each value. I personally prefer grabbing the ids with simple ->where queries and utilise ->whereIn approach.
I found solution for my situation
$students = Student::with(['studyfields' => function ($q) use ($studyStatusId) {
$q->whereHas('pivot')->where('study_status_id', $studyStatusId);
}])
->whereHas('studyfields', function ($q) use ($facultyAbbreviation) {
$q->whereHas('studyprogram', function ($q) use ($facultyAbbreviation) {
$q->where('faculty_abbreviation', $facultyAbbreviation);
});
})
->get();
$students = $students->filter(function ($student) {
return count($student->studyfields) > 0;
})->values();
Query above fetch all students from specific faculty and if studyfields array doesn't contains specific study_status, leave empty array so later I can filter collection from empty arrays assuming that each student belongs to at least one studyfield.

Yii2 viaTable multiple variables

I'm trying to find a number of UserComment using a viaTable on the table called user_comment_user in Yii2. However I can't seem to get my variables/query inserted properly.
Currently I've got two queries set up, to check if (on their own) they achieve the correct result, which they do.
These are the two queries that somehow have to be merged into one:
public function findConversation($id)
{
$query = $this->hasMany(UserComment::classname(), ['id'=>'user_comment_id'])
->viaTable('user_comment_user', ['sender_id'=>'id'], function ($query) use ($id) {
$query->andWhere(['receiver_id'=>$id]);
});
$query2 = $this->hasMany(UserComment::classname(), ['id'=>'user_comment_id'])
->viaTable('user_comment_user', ['receiver_id'=>'id'], function ($query) use ($id) {
$query->andWhere(['sender_id'=>$id]);
});
return $query;
}
The answer was actually much simpler than I had imagined:
public function findConversation($id)
{
$query = UserComment::find();
$query->leftJoin('user_comment_user', 'user_comment_user.user_comment_id=user_comment.id');
$query->where(['receiver_id'=>$this->id, 'sender_id'=>$id]);
$query->orWhere(['receiver_id'=>$id, 'sender_id'=>$this->id]);
$query->orderBy(['created_at'=>SORT_DESC]);
return $query;
}

Laravel 4 Advanced Where - Unknown Column

I have a query which looks like this:
$items = Item::live()
->with('location')
->where('last_location_id', Input::get('last_location_id'))
->get();
The background of this is...
2 tables: Items & Cars.
The live scope is:
public function scopeLive($query)
{
return $query->whereHas('basic_car', function($q)
{
$q->whereNotNull('id')->where('sale_status', 'Live');
});
}
This basically checks the cars table for a matching id to that of the items 'car_id' field and will run some where clauses on the cars table.
I now however want to check another field on the cars table, but using the Input::get('last_location_id') from the original query.
$items = Item::live()
->with('location')
->where('last_location_id', Input::get('last_location_id'))
->orWhere('ROW ON THE CARS TABLE' = Input::get('last_location_id'))
->get();
This does't work, then I tried:
$items = Item::live()
->with('location')
->where('last_location_id', Input::get('last_location_id'))
->orWhere(function($query)
{
$query->where('cars.Location', Input::get('last_location_id'));
})
->get();
Which results in an unknown column 'cars.Location' error.
My next test was to create another scope:
public function scopeLiveTest($query)
{
return $query->whereHas('basic_car', function($q)
{
$q->whereNotNull('id')->where('sale_status', 'Live')->where('Location', 1); // hardcoded ID
});
}
And replacing the live() scope with that works but I dont get the affect of the orWhere in the query itself and I also cannot specify a ID from the Input.
How can I do this?
You can pass a parameter to scope like this:
$items = Item::liveAtLocation(Input::get('last_location_id'))
->orWhere(function( $query ) { // to get the OR working
$query->live()
->with('location')
->where('last_location_id', Input::get('last_location_id'));
})
->get();
And for the scope:
public function scopeLiveAtLocation($query, $location_id)
{
return $query->whereHas('basic_car', function($q) use ($location_id)
{
$q->whereNotNull('id')->where('sale_status', 'Live')->where('Location', $location_id);
});
}

Categories