雄辩的反向关系执行无用的查询

时间:2016-10-18 13:39:32

标签: php laravel eloquent

我与反向方法有一个标准的一对多关系:

class Place 
{
  public function hotels()
  {
    return $this->hasMany('Hotel');
  }
}

class Hotel 
{
  public function place()
  {
    return $this->belongsTo('Place');
  }
}

一切正常。

在我的控制器中,我需要在一个方框中显示每个酒店的列表,所以我创建了类似的东西:

//place.blade
<h1>{{ $place->name }}</h1>
@foreach($place->hotels as $hotel)
  @include('partials.hotels-box', ['hotel' => $hotel])
@endforeach

//partials/hotels-box.blade
<a href="route('hotelRoute', ['hotel' => $hotel->name, 'place' => $hotel->place->name) }}">
  {{ $hotel->name }}
</a>
@endforeach

一切正常。

但是,在hotels-box.blade里面,每当我访问$hotel内的地方模型时,Eloquent都会启动一个新查询(即使该地点总是相同的,它也是触发{{ {1}})。

我想到的唯一想法是避免这些问题:

  • @foreach传递给部分并使用它而不是$place
  • 使用循环
  • 在控制器中手动设置$hotel->place

但我不喜欢他们。

3 个答案:

答案 0 :(得分:1)

您可以将$place模型与$hotel一起传递到部分视图。然后,不是使用反向关系来获取地名,而是可以通过$place->name得到它。

或者,如果由于某种原因你不喜欢这样做,你也可以在查询中急切加载反向关系:

$places = Place::with('hotels', 'hotels.place')->get();

答案 1 :(得分:0)

您似乎遇到的是一个急切的加载问题,也称为n + 1。酒店模型尚未加载Place关系,因此每次都必须执行新查询。

最简单的解决方案似乎是你可以在你的部分中调用$place->name而不是$hotel->place->name,但这只能解决问题,如果只从你的代码中调用部分。提供。如果从$place不存在的其他地方调用它,那么您将不得不解决急切的加载问题。

您应该能够像控制器那样急切地加载控制器:

$place = Place::with('hotels.place')->get ()

请注意,点符号用于添加关系的子关系。因此,原始html将无需任何更改即可使用,我们只添加了一个新查询。

答案 2 :(得分:0)

如果在从DB获取“模型”值时未获取关联数据,则每次访问该数据时都会进行查询。要解决此问题,您应该在从db获取时获取关联数据。

例如,当你写:

 $palce = Place::all();

它仅查询以获取places表的所有行。需要注意的是,此查询不会提取任何关联的hotel。 如果你写,

$hotels = $place->hotels;

将对db进行另一个查询,以获取与该hotels相关联的$place表的所有行。 然后写下如下:

foreach($palce->hotels as $hotel){
   $palce = $hotel->place;
}

然后,在每次迭代时foreach循环内部,都会进行新的JOIN查询,以便与place关联hotels

解决方案1: 您可以在执行第一个查询时加载所有必需的数据,如下所示:

$places = Place::with('hotels', 'hotels.place')->get();

但是这个解决方案的问题是,你得到hotels by place,然后得到相同的place by hotels。由于您已经拥有join,因此无需place次查询,无需再次获取place by hotels

解决方案2(更好):

$places = Place::with('hotels')->get();

在视图中,做这样的事情:

@foreach($place->hotels as $hotel)
  @include('partials.hotels-box', ['hotel' => $hotel, 'place' => $place])
@endforeach

然后,在$palce中使用partials.hotels-box。我想你明白了我的意思。

祝你好运。