bounty将在3天后过期。回答此问题可获得+100声望奖励。abr希望吸引更多人关注此问题。
假设我有一个名为Research
的模型,每个研究都属于许多Location
模型,每个Location
模型也属于许多Contact
模型,但是,每个Contact
也与Research
相关。
class Research extends Model {
protected $table = 'researches';
public function locations()
{
return BelongsToMany( Location::class, 'research_locations_list', 'research_id', 'location_id' );
}
}
class Location extends Model {
protected $table = 'locations';
public function researches()
{
return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
}
public function contacts()
{
return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
}
class Contact extends Model {
protected $table = 'contacts';
public function locations()
{
return BelongsToMany( Location::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
}
researches
表格:
+----+------------+
| id | research |
+----+------------+
| 1 | Research 1 |
| 2 | Research 2 |
+----+------------+
locations
表格:
+----+---------------+
| id | location |
+----+---------------+
| 1 | United States |
| 2 | Great Britain |
| 3 | Germany |
+----+---------------+
contacts
表格:
+----+---------+
| id | contact |
+----+---------+
| 1 | Jack |
| 2 | John |
| 3 | Hanz |
+----+---------+
research_locations_list
表格:
+----+-------------+-------------+
| id | research_id | location_id |
+----+-------------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
| 4 | 2 | 3 |
+----+-------------+-------------+
因此,研究1在美国和英国进行,研究2在英国和德国进行location_contacts_list
表格:
+----+-------------+------------+-------------+
| id | location_id | contact_id | research_id |
+----+-------------+------------+-------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | 1 | 2 |
| 4 | 3 | 3 | 2 |
+----+-------------+------------+-------------+
研究1应将Jack和John作为在美国的联系人,而在其他地方没有联系人;
研究2应该让John作为英国的联系人,让Hanz作为德国的联系人;
现在,使用延迟加载可以实现:
$researches = Research::all();
foreach( $researches as $research )
{
foreach( $research->locations as $location )
{
$contacts = $location->contacts()->wherePivot( 'research_id', $research->id )->get();
// Will return John and Jack in United States for Research 1 and John in Great Britain and Hanz in Germany for Research 2
}
}
现在,问题是:我如何通过快速加载实现这一点?
$researches = Research::with( 'locations.contacts' )->all();
foreach( $researches as $research )
{
foreach( $research->locations as $location )
{
$contacts = $location->contacts;
// Will return John and Jack in United States, John in Great Britain ( which is not supposed to happen ) for Research 1 and John in Great Britain and Hanz in Germany for Research 2
}
}
也许我可以指示以某种方式为联系人尊重祖先id?像:
$research = Research::with( 'locations.contacts' )->where( 'researches.id = location_contacts_list.research_id' )->all();
更新
最接近解决这个问题的方法是修改Location
模型,如下所示:
class Location extends Model {
protected $table = 'locations';
public function researches()
{
return BelongsToMany( Research::class, 'research_locations_list', 'research_id', 'location_id' );
}
public function contacts()
{
return BelongsToMany( Contact::class, 'location_contacts_list', 'location_id', 'contact_id' );
}
// Modify contacts attribute getter
public function getContactsAttribute()
{
$contacts = $this->contacts();
if( !empty( $this->pivot->research_id ) )
{
$contacts = $contacts->wherePivot( 'research_id', $this->pivot->research_id );
}
return $contacts->get();
}
}
但看起来有点脏...
2条答案
按热度按时间mjqavswn1#
如果我没猜错的话,你需要在
with
语句中添加一些条件。如果你想使用有说服力的语法,你可以这样做:请记住,由于在
with
中使用了嵌套关系,如locations.contacts
,查询中的where
函数将只筛选最后一个模型(在本例中为contacts
)。如果要基于某些条件同时筛选位置和联系人,则必须编写类似以下内容的代码(仅作为示例):不过,为了实现这一点,您还需要创建与数据透视表的关系(如果您还想在条件中使用它)。否则,您必须使用不同的语法,即使用联接。请查看www.example.com上查询构建器文档中的此页面https://laravel.com/docs/9.x/queries#main-content
s4n0splo2#
在您的解决方案中,您遇到了N+1查询问题。我可以建议以下解决方案:
这里总是只有3个对数据库的查询。并且只有必要的模型会被加载