1

I have a problem with Laravel 5.3 eager loaded relations using method with().

Either I do something wrong, or I understand it wrong.

I run this code:

$result = Post::with(['comments' => function ($query) {

    $query->where('content', 'like', '"%blanditiisx%"');

}])->get();

By checking database table 'comments' empirically, I know, that there is only single comment that has word 'blanditiisx' in its column 'content'.

So, since given comment can belong only to one post and we have only one comment matching 'where' condition, I was looking to get only one post.

To my surprise, above code returns all posts I have in database.

I would appreciate, if someone could tell me where I am going wrong with it.


Here is what I am working with:


POST

I have Post class with this relationship defined in it:

/**
 * One to Many relation
 *
 * @return \Illuminate\Database\Eloquent\Relations\hasMany
 */
public function comments()
{
    return $this->hasMany(Comment::class);
}

It works with this posts table:

+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
| title      | varchar(255)     | NO   |     | NULL    |                |
| slug       | varchar(255)     | NO   | UNI | NULL    |                |
| summary    | text             | NO   |     | NULL    |                |
| content    | text             | NO   |     | NULL    |                |
| seen       | tinyint(1)       | NO   |     | 0       |                |
| active     | tinyint(1)       | NO   |     | 0       |                |
| user_id    | int(10) unsigned | NO   | MUL | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

COMMENT

I have Comment class with this relationship defined in it:

/**
 * One to Many relation
 *
 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function post()
{
    return $this->belongsTo(Post::class);
}

It works with this comments table:

+------------+------------------+------+-----+---------+----------------+
| Field      | Type             | Null | Key | Default | Extra          |
+------------+------------------+------+-----+---------+----------------+
| id         | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | timestamp        | YES  |     | NULL    |                |
| updated_at | timestamp        | YES  |     | NULL    |                |
| content    | text             | NO   |     | NULL    |                |
| seen       | tinyint(1)       | NO   |     | 0       |                |
| user_id    | int(10) unsigned | NO   | MUL | NULL    |                |
| post_id    | int(10) unsigned | NO   | MUL | NULL    |                |
| deleted_at | timestamp        | YES  |     | NULL    |                |
+------------+------------------+------+-----+---------+----------------+

EDIT:

Actually my initial code works, it just returns results in a way that got me confused.

What it does is it returns one collection for each post.

Most of them are empty. Only those collections, that fall within constraints of query are filled with data.

Rest are empty, but still fetched, so to filter them out one may use count().

And of course, they are eager.

2 Answers 2

4

You can try whereHas() for this as:

$result = Post::whereHas('comments', function ($query) {

    $query->where('content', 'like', '"%blanditiisx%"');

})->with('comments')->get();

It allows adding customized constraints to a relationship constraint, such as checking the content of a comment.

Or try as:

Post::whereHas('comments', function ($query) {
            $query->where('content', 'like', '"%blanditiisx%"');
        })
        ->with(['comments' => function ($query) {
            $query->where('content', 'like', '"%blanditiisx%"');
        }])
        ->get();

Docs

Sign up to request clarification or add additional context in comments.

6 Comments

I know has()/whereHas() works, but will that still be eager loaded?
Nope, thats why I have added with('comments') in the query. But if you want to get only filtered comment then add the where constraints in with also.
You can use the combination of whereHas and with to get the result.
So, what you are saying, by chaining with('comments') I make this loaded eagerly, right? ----- As to my (not working example) - in Laravel docs laravel.com/docs/5.3/eloquent-relationships#one-to-many, if you find on that page 'Constraining Eager Loads', you will find: "Sometimes you may wish to eager load a relationship, but also specify additional query constraints for the eager loading query. " -- so it seems to suggest, that it should be possible to fine-craft query. They even give example as mine - which do no work.
Thank you for your time. I appreciate it. Your answer just pushed me in the right direction. Actually my initial code works, it just returns results in a way that got me confused. What it does is it returns one collection for each post. Most of them are empty. Only those collections, that fall within constraints of query are filled with data. Rest are empty, but still fetched, so to filter them out one may use count(). And of course, they are eager. Thanks again.
|
0

The with() method is for preloading related data, not filtering the query you already have. See the whereHas() method for the filtering you're looking for.

2 Comments

According to Laravel docs laravel.com/docs/5.3/eloquent-relationships#one-to-many, if you find on that page 'Constraining Eager Loads', you will find similar example with explanation: "Sometimes you may wish to eager load a relationship, but also specify additional query constraints for the eager loading query. " -- so it seems to suggest, that it should be possible to fine-craft query.
That's constraining the "eager loading query". In your example, that means constraining which comments you want to load. You seemed to expect it to constrain which posts would be loaded. That's what whereHas() is for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.