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].
Related
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();
I have roles and admins table and pivot table for admin_role
roles table structure
id, role_name, slug
admins table structure
id, name, email, phone
admin_role table structure
id, role_id, admin_id
Admin.php Model
public function roles() {
return $this->belongsToMany(Role::class,'admin_role');
}
Role.php Model
public function admins() {
return $this->belongsToMany(Admin::class,'admin_role');
}
AdminController.php
public function getTeams() {
try {
$selectedName = request('name');
$selectedRole = request('role');
$selectedPhone = request('phone');
$admins = Admin::with(['roles','resources'])
->when($selectedName, function($query) use($selectedName) {
$query->where('name', 'like', '%' .$selectedName.'%');
})
->when($selectedPhone, function($query) use($selectedPhone) {
$query->where('phone_number', $selectedPhone);
})
->when($selectedRole, function($query) use($selectedRole) {
$query->whereHas('roles', function ($q) use($selectedRole){
$q->whereIn('role_id', $selectedRole);
});
})
->orderBy('id', 'desc')
->paginate(10);
return response()->json($admins);
} catch (\Illuminate\Database\QueryException $e) {
$e = "Get teams Api Error";
return response()->json($e);
}
}
The above query was working fine, it was filtering data based on selected Name, Phone but after adding whereHas clause for role-based filtering, it returns empty array. Filtering works only when I passed data in selected Role.
Any suggestions on where I made the mistake.
I have added my UI of admin table.
without applying any of the filter like (name, email, phone, role) it should display data based on default query that i have added. Once the filter option are selected it should show data based on that filter parameter.
My problem : If I passed role than the data gets displayed in table but if there is no any filter selected than there is empty table rather than default data.
After I removed below query from AdminController data is returned to UI table but if I add it, then data gets returned only if role are selected.
->when($selectedRole, function($query) use($selectedRole) {
$query->whereHas('roles', function ($q) use($selectedRole){
$q->whereIn('role_id', $selectedRole);
});
})
I think you can solve you issue by mentioning table name admin_role in query like below
$q->whereIn('admin_role.role_id', $selectedRole);
or
$q->whereIn('admin_role.id', $selectedRole);
Updated
->when((isset($selectedRole)&&count($selectedRole)), function($query) use($selectedRole) {
$query->whereHas('roles', function ($q) use($selectedRole){
$q->whereIn('admin_role.id', $selectedRole);
});
})
I have the following query:
$countries = Country::where('code', '=', $code)
->with(array('cities.details' => function ($query) use ($user) {
$query->where('cities.name', '=', 'This is a city name');
}))->first();
Lets see the example: I have three tables, Country, City and CityDetails. I want to get all countries, then get all cities (INCLUDING the details information) but I also want to filter cities by name and fetch details table which belongs to the city table.
If I want to use with to get the child and another table using with(cities.details), how can I filter using CITIES attributes?
The main question is: How can I fetch two tables in a with statement like secondTable.OtherTable and filter the query using secondTable attributes?
Just for make it clearer, if I use statement like this:
$countries = Country::where('code', '=', $code)
->with(array('cities.details' => function ($query) use ($user) {
$query->where('name', '=', 'This is a detail name');
}))->first();
I can access only details table attributes. The question is: How can I access the city table attribute to filter inside a with a statement?
Thanks in advance
I just found a solution. Basically, I should apply the filter for city table and then call wit function on the subquery. This code solved my problem:
$countries = Country::where('code', '=', $code)
->with(array('cities' => function ($query) use ($user) {
$query->where('name', '=', 'San Francisco')->with('details');
}))->first();
Note that I called with('details') on city only after filter in subquery.
The most straightforward one is using join query:
(new Country)
->join('cities', 'cities.id', '=', 'countries.city_id')
->join('city_details', 'cities.id', '=', 'city_details.city_id')
->where('cities.name', 'TheName')
->where('city_details.info', 'info')
->select('...');
But the result has only one level. So,
$query = (new Models\Country())
->with([
'cities' => function ($query) {
$query->where('name', 'xxx')->with([
'details' => function ($query) {
$query->where('info', 'info');
}
]);
}
]);
$result = $query->get()->filter(function ($item) {
return count($item['cities']);
});
The result gives countries with empty cities. So use Laravel collection to filter in the last.
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.
I have a little problem, I have a table with Movies and another with Genres ex: Action, Adventure etc...
Table names:
movies
movie_genre
genres
I want to fetch all the Movies with the selected Genre ex: Action
$movies = Movie::with('genres')
->when($genre, function ($query) use ($genre) {
return $query->where('genres.id', $genre->id);
})
->paginate(20);
This php code doesn't work, how can I make it work?
PS: The genre is transfered from the view by $_GET['genre'] and stocked in the variable $genre.
Edit after the answer of #Shane and #Marcin NabiaĆek :
The relationship is set in the models and when I use your code directly like this:
Movie::with('genres', 'countries', 'type')
->when($type, function ($query) use ($type) {
return $query->where('medias.type_id', $type->id);
})
->whereHas('genres', function ($query) use ($genre) {
return $query->where('genres.id', $genre->id);
})
->paginate(20);
it work perfectly, but with the when() function it dosn't. I have to use the when() to make the condition work only if there is a genre selected by the user.
You should use whereHas to get all movies that are assigned to given genre:
$movies = Movie::with('genres')
->when($genre, function ($query) use ($genre) {
$query->whereHas('genres', function($q) use ($genre) {
return $query->where('id', $genre->id);
});
})->paginate(20);
In case $genre is only id and not model, you should rather use:
return $query->where('id', $genre);
and in case it's an array (user is allowed to choose multiple genres), you should use:
return $query->whereIn('id', (array) $genre);
I'm not familiar with the when function. I use whereHas:
$movies = Movie::whereHas('genres', function ($query) use ($genre) {
$query->where('id', $genre->id);
})->with('genres')->get();
Also, is $genre the model or the ID? You mentioned it was received from $_GET which would mean it would be an ID probably.
I think a better solution would be to use your relationship function in the Genre model to get them:
$movies = Genre::find($genre_id)->movies;