Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
285 views
in Technique[技术] by (71.8m points)

php - Eloquent Nested Relation with Some Constraint

I have the following three tables:

A
-------------
| id | name |
-------------

B
--------------------
| id | A_id | name |
--------------------

C
--------------------
| id | B_id | name |
--------------------

So, the data in table C belongs to the data in table B which belongs to the data in table A. Now, I want to query C, while also retrieving data from B and A and the following code does the trick just fine.

C::with('B.A')->get();

The problem now, is that I want to query C with some constraints. One of these constraints is the id of A. I've tried the following:

C::with(array('B.A' => function ($query)
{
    $query->where('id', '=', $constraint);
}))->get();

But it seems that Eloquent will retrieve all the rows in C without even taking the constraint into account, except when it's executing the query to retrieve data in table A. How do I get around this problem? Do I need to add another field in C, that is A_id, and match $constraint against that field?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are confusing the with() method with SQL's JOIN and that happens a lot.

First a little background

When you use Foo::with('bar')->where_something(1), Laravel will first load the Foo and then, based on Foo.bar_id, it will load the Bar. It serves the purpose of telling Laravel to eager load dependencies of your model on a combined query, greatly improving performance of iterations on those models.

If you don't use it, the following queries should be executed:

SELECT * FROM foos WHERE foos.something = 1;
SELECT * FROM bars WHERE bars.id = 30;
SELECT * FROM bars WHERE bars.id = 57;
SELECT * FROM bars WHERE bars.id = 134;
SELECT * FROM bars WHERE bars.id = 1096;

If you use it, on the other hand:

SELECT * FROM foos WHERE foos.something = 1;
SELECT * FROM bars WHERE bars.id IN (30, 57, 134, 1096); // Eager loading

When you add a condition to that with(), you are only constraining the eager loading of those dependencies, and not the first query.

Now to your answer

To achieve what you want, you'll need to use ->join().

C::with(array('b', 'b.a'))
 ->join('b', 'b.id', '=', 'c.b_id')
 ->join('a', 'a.id', '=', 'b.a_id')
 ->where('a.id', '=', $ID)
 ->get('c.*');

I've included the with(), because I didn't know if you would need to access $c->b->a. If you don't, and you just need $c data, you can remove the with() since it will query for B's and A's unnecessarily.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...