Adding where() on with() eager loading. - php

I have a little but intricate problem here, I'll try to explain it as best I can.
First I have 3 models, Course, Student, and Quiz,
Student and Course are in a Many to Many relationship
Student and Quiz are in a Many to Many relationship
Quiz and Course are in a One to Many relationship respectively
And I have this following query:
$course = Course::whereSlug($slug)->first(); // Some Course
$quizzes = $course->students()->with('quizzes'); // <-- Here lies the problem.
In the last sentence I want to edit this query to be something like this:
$quizzes = $course->students()->with('quizzes)->where('course_id', $course->id);
I want to do it like that because I want to only grab the quizzes that are related to both the Student model and the Course model.
To give you the full picture, after that I loop through the $students variable in a vue component like this:
<div v-for="student in students"></div>
I am looping with the Student model Because I'm also retrieving different properties other than the quizzes.
But of course when I do it like the query up there I end up retrieving all quizzess for the all students that has a course_id = $course_id.
Required
I want to filter the results to get the quizzes of a student ONLY if they have a course_id of whatever the current courses's id is.

You can use whereHas function to do your job, something like this:
$quizzes = $course
->students()
->whereHas('quizzes', function($q){
$q->where('course_id', $course->id);
})
->with('quizzes)
->get();
I think if you follow the convention you should take Quiz model with students and course cross checked, its upto you. You can find out more on Laravel Documentation Hope this resolves your problem.

Of course after I posted the question I found the answer online randomly, here it is:
$quizzes = $course->students()->with(['quizzes'=> function($query) use ($course) {
$query->where('course_id', $course->id);
}])->get();

Related

internal orderBy gets overwritten by external orderBy in Laravel Eloquent

I have a reliationship User hasOne Position and I'm fetching the users, but I want to sort them first by Position->name and then by User->name. I tried the following
<?php
$sorted = Position::where('groupId', $this->groupId)
->whereIn('id', $positions)
->with(['user' => function($query) {
$query->orderBy('user.name'); // internal sort
}])
->orderBy('position.name') // external sort
->get();
This way the results are sorted by the external sort only, or, by Position->name. Different users with the same Position->name are listed unsorted. If I remove the external sort, and leave only the sortBy User->name, it works, BUT only for the names, while positions are random.
I have tried different ways
setting the order in the Position->user relationship, does not work
setting the order in the User->position relationship, does not work
defining only an external orderBy('position.name, user.name'), crashes, saying user table is not in the query.
I also tried following similar questions like
Laravel orderBy on a relationship
Ordering Related Models with Laravel/Eloquent
but they don't seem to be trying to sort the results both by the parent and the relationship. It seems my only solution is to walk the result and sort them in PHP instead of from the DB, but this sounds dumb.
Please advice, thank you.
When you want to sort the parent Position by the relationship User, you need to use a join():
$sorted = Position::where(...)
->whereIn(...)
->with('user')
->join('users', 'users.id', '=', 'positions.user_id') // Or whatever the join logic is
->orderBy('users.name')
->orderBy('positions.name')
->get();
Note: The orderBy() on the user relationship within with() doesn't seem necessary, as by convention, a singular-named relationship should only return 1 record, and sorting on a single record is pointless.
This will return a Collection of Position models, with an attached User model, sorted by the User's name, then the Position's name. You might need to add a select('positions.*') to avoid any ambiguity issues, but this should give you the general idea.

Laravel select from DB where in Collection - Laravel Nova

I have a DB, "views," with many, many entries. I also have a "Courses" table, which these views are one-many related to. In Laravel Nova, I can get a metric of all views over time for a course with some code like this:
public function calculate(Request $request)
{
return $this->countByDays($request, view::where('viewable_id', $request->resourceId));
}
In this case, viewable_id is the id of the course, and $request->resourceId gives the ID of the course to sort by. Pretty simple.
However, now things get a little difficult. I have another model called Teachers. Each Teacher can have many courses, also in a one-many relationship. How do I get a metric of views over time for all the courses that teacher teaches?
I assumed the simplest way to do this would be to create a Laravel Collection with all courses the Teacher teaches (not exactly efficient), and then select all views in the database where viewable_id matches one of the courses in that list. Of course, by posting this, I couldn't figure out how to do that.
Of course, once this is figured out, I'd love to do the same thing for Categories (though that should function in a very identical manner to Teachers, so I don't need to ask that question).
How do I get a metric of views over time for all the courses that teacher teaches?
This should be the "countByDays" of views where the viewable_id is in the list of course ids that the teacher teaches.
An SQL query statement to achieve that is given below:
select * from "views"
where "viewable_id" in (select "id" from "courses" where "teacher_id" = ?)
The Eloquent query should be similar to:
$this->countByDays($request,
view::whereIn(
'viewable_id',
Course::with('teacher')
->select('id')
->where('teacher_id', $request->resourceId)
)
);

Return all data from hierarchical belongsToMany relationship Laravel 5.3

I have models that are Netflix-esque (in the sense of hierarchy). As an example, I have a Lesson which belongsToMany Course which belongToMany Collection, which belongsToMany Subgroup, which belongsToMany Group. Lesson is the lowest level, up to Group as the top level. Going down the chain, each one belongsToMany of the next link down as well.
I am using a filter button that will make a call from Wordpress to my Laravel API. When I pass the group id and the subgroup id, I need to be able to return the collections belonging to the subgroup. But what I need is something like:
$group->with('subgroups')->where('subgroup_id')->with('collections')->with('courses')->with('lessons');
However, that kind of syntax doesn't work. Is there a way to query each level down and get that level's relationships?
If more code is needed, I'd be happy to share more.
The following is untested, but should hopefully either work immediately, or give you an idea as to how to solve the problem yourself.
A couple points:
You can chain relationships within a with call. E.g. courses.lessons will get all courses and related lessons for the collections found.
whereHas allows you to query relationships. In this example, I am looking for all collections, with a subgroup that matches the subgroup ID passed, and that also have a group that matches the group ID passed.
Example:
$groupId = 123;
$subgroupId = 456;
$collections = Collection::with('courses.lessons')
->whereHas('subgroup', function ($query) ($groupId, $subgroupId) {
return $query->whereHas('group', function ($query) use ($groupId) {
return $query->where('id', $groupId);
})
->where('id', $subgroupId);
})
->get();

querying many-to-many-relationship laravel

I have a many-to-many relationship with models Competition and Season with a pivot table named competition_season.
Season->belongsToMany->Competitions->hasMany->Teams->hasMany->Players
I want to query the data of all players in a team in a specific season.
something like
\App\Player::with('team.competition.season')->where('team.competition.season.id', 2);
However since season is a collection (competition has many seasons) at this moment i can never query it like this.
Can someone point me in the right direction to properly select all players in a team in a particular season?
First, you need to add relation hasManyTrough at your model (read more about at: has-many-through)
Then, all you need is "whereHas"
$players = \App\Player::with('season')->whereHas('season', function($q){
$q->where('season.id', '=', 2);
})->get();

Laravel 4 Conditional Relationship

I need to be able to specify conditions on a relationship (one-to-many).
Example being; We have a Post which has many Comments, but I only want the Posts with Comments made by User A and then return the Post object(s).
The only way I can currently think of doing this is with a Fluent query, which wouldn't return the Post object I desire!
EDIT:
The comment has to of been made by User A. The relationship of the User who made the Comment to Post isn't a direct one. It would go through the Comment table.
RE EDIT:
Would it also be possible to say have distinct Posts? Rather than return 3 of the same Post Object?
You can query relationships. You would end up with something like this:
$postsWithComments = $user->posts()->has('comments', '>=', 1)->get();
Here is an extract from documentation: http://laravel.com/docs/eloquent
Querying Relations
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method:
Checking Relations When Selecting
$posts = Post::has('comments')->get();
You may also specify an operator and a count:
$posts = Post::has('comments', '>=', 3)->get();

Categories