Laravel 4 Conditional Relationship - php

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();

Related

Adding where() on with() eager loading.

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();

Get all related many-to-many from all records instead from each record laravel

I have posts table related with tags table via post_tags. I want to get all tags from all user post. let's say a user have 3 posts, then I want to get all tags from 3 of them without loop in user post to get individual related tags of each post, it's like join + distinct but maybe there is method in Eloquent to make it easy. Thanks, let me know if my question is unclear or duplicate.
Use whereHas():
$tags = Tag::whereHas('posts', function($q) use($userId) {
$q->where('user_id', $userId);
})->get();

Laravel 5 - ORM has() Relationship (Inverse - notHas)

In the Laravel 5 I can find all posts that have at least one comment using has method:
// Retrieve all posts that have at least one comment...
$posts = App\Post::has('comments')->get();
As theres no method like notHas, how can I find all posts that have no comments?
(remembering that it's a hasMany relationship)
Reference:
Querying Relationship Existence
http://laravel.com/docs/5.1/eloquent-relationships#querying-relations
Old topic but updated answer for those who came from Google like me. Nowadays you can just use doesntHave().
$posts = App\Post::doesntHave('comments')->get();
Reference:
Querying Relationship Absence
https://laravel.com/docs/5.7/eloquent-relationships#querying-relationship-absence
There isn't a notHas() method as far as I'm aware, but finding records with a relationship count less than one usually satisfies this requirement:
$posts = App\Post::has('comments', '<', 1)->get();

Laravel n+ issue eager loading count()

I'm going through my laravel application and trying to fix any n+ issues I can find. I have come across one scenario which isn't really an n+ but not sure what to call it.
I have 2 models Post, Comment. A post has many comments and a comment belongs to a post
When I loop through all my posts I would like to display a count of how many comments they contain. I've been able to do this fine. But the problem it is 2 queries.
How do I update the following Eloquent query to add a column for comments count.
Post::where('status', 1)->get();
Thanks
Update
As of Laravel 5.2.32, a new method was added to the query builder to help with this. When you add the withCount($relation) method to your query, it will add a {relation}_count field to the results, which contains the count of the supplied relation.
So, your query would be:
$posts = Post::where('status', 1)->withCount('comments')->get();
foreach($posts as $post) {
echo $post->comments_count;
}
You can read more in the documentation here.
Original
#JarekTkaczyk has a good blog post that does what you're looking for. Check out the article here.
Basically, you'll be creating a relationship that contains the count of comments for the post, and you'll eager load the relationship (thus avoiding the n+1). He also has some syntactic sugar in there for accessing the count through an attribute accessor.
Either just use count on the relationship, or if you think it's necessary, you could add a 'num_comments' to the Post model and increment it on the creation of a comment:
$post->comments()->count();
or in the comments model:
public function create( $commentData ){
$result = $this->fill( $commentData );
$this->post()->increment('num_comments');
return $result;
}

One-to-many then eager load an array with Laravel Eloquent ORM

With Laravel and the eloquent ORM, I want to create an array or object of all posts and corresponding comments that belong to a specific user (the logged in one). The result will then be used with Response::eloquent(); to return JSON.
Basically in pseudo-code:
All Posts by user ::with('comments').
or
Posts by Auth::user()->id ::with('comments').
I have my database setup per the usual with a user's table, comments table and posts table. The comments table has a post_id and the posts table has a user_id.
The long way of doing this without Laravel would be something like:
SELECT * FROM posts WHERE user_id = 'user_id'
foreach($result as $post) {
SELECT * FROM comments WHERE posts_id = $post->id
foreach($query as $comment) {
$result[$i]->comments[$n] = $comment
}
}
But I want to accomplish it with Laravel's Eloquent ORM.
It looks like you don't even need a nested eager load, you just need to modify the query that with returns, so:
$posts = Post::with('comments')->where('user_id', '=', 1)->get();
You can daisy chain most of the methods in the Eloquent system, generally they're just returning a Fluent query object.
(I haven't tested it but I'm fairly certain that'll work. Also, you can't do it on ::all() because that calls ->get() for you. You have to dig in the source code to find this, I don't think the Eloquent documentation mentions that's what it's doing.)
Also the Eager Loading Documentation covers nested eager loading, so you could load all users, with their posts, with the comments:
You may even eager load nested relationships. For example, let's
assume our Author model has a "contacts" relationship. We can eager
load both of the relationships from our Book model like so:
$books = Book::with(array('author', 'author.contacts'))->get();

Categories