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

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

Related

How can this be done with the relationship, and is it worth it? (Get all departments for clinic)

I have 3 tables:
clinics
departments
clinics_in_departments
Using Query Builder:
$department = ClinicsInDepartment::whereIn('clinic_id',[1,2,3])
->join('departments', 'clinics_in_departments.department_id', '=', 'departments.id')
->get();
How can this be done with the relationship, and is it worth it?
If you look at the documentation of Laravel at the Many to Many section https://laravel.com/docs/5.6/eloquent-relationships#many-to-many it's already explained in there. If you're planning to keep using Laravel I would recommend using the best practises of Eloquent. It's easier to understand and read for other developers. It's always worth to make your product the best you can. It also gives possibilities to quickly extend and maintain your application.
All you need to do is to define a relationship in your model clinics
// second, third and fourth parameter could also be optional
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments', 'department_id', 'clinic_id');
}
To retrieve the data you can use
$clinics = Clinics::with('departments')->get();
// this would hold a list of departments for each clinic
To get exactly the same data extend the query to this
$clinics = Clinics::with('departments')->whereIn('clinic_id',[1,2,3])->get();
Because it's a Many to Many relationship you could also define a relationship for the model Departments and do exactly the same as mentioned above.
You can define a belongs to many relation inside Clinics model like below code
function departments(){
return $this->belongsToMany('App\Clinics', 'clinics_in_departments');
}

How to join three tables with eloquent using a model

I've never studied Eloquent. I am studying APIs and I want to get some data as kickly as possible, without writing sql, what is I usually do.
I have the tables: "house" related to "announcement" (one to one) and the "announcement" table is related to "advertiser" (one to many).
When I tried
$house = \App\House::where('house_id', $id)->with('announcement', 'advertiser')->first();
I get the error: announcement undefined method.
I think I set up the relationship on the models correctly so I didn't include them here. I want to join using the model House because I am not using the default connection when using Eloquent.
with() must be placed at first, and specify your relationships in an array (if you load more than one)
$house = \App\House::with(['announcement', 'advertiser'])->where('house_id', $id)->first();
Edit :
Sorry, i haven't seen that it's a nested relationship
You can use this code instead of the above one :
$house = \App\House::with(['announcement', 'announcement.advertiser'])->where('house_id', $id)->first();
for a nested relationship use dot like announcement.advertiser
$house = App\House::with(['announcement', 'announcement.advertiser'])->where('house_id', $id)->first();

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;
}

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

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