Laravel - 热切加载多态关系的相关模型 [英] Laravel - Eager Loading Polymorphic Relation's Related Models
问题描述
我可以在没有任何 n+1 问题的情况下急切加载多态关系/模型.但是,如果我尝试访问与多态模型相关的模型,则会出现 n+1 问题,而且我似乎无法找到解决方法.这是在本地查看它的确切设置:
I can eager load polymorphic relations/models without any n+1 issues. However, if I try to access a model related to the polymorphic model, the n+1 problem appears and I can't seem to find a fix. Here is the exact setup to see it locally:
1) 数据库表名/数据
历史
公司
产品
服务
2) 模型
// History
class History extends Eloquent {
protected $table = 'history';
public function historable(){
return $this->morphTo();
}
}
// Company
class Company extends Eloquent {
protected $table = 'companies';
// each company has many products
public function products() {
return $this->hasMany('Product');
}
// each company has many services
public function services() {
return $this->hasMany('Service');
}
}
// Product
class Product extends Eloquent {
// each product belongs to a company
public function company() {
return $this->belongsTo('Company');
}
public function history() {
return $this->morphMany('History', 'historable');
}
}
// Service
class Service extends Eloquent {
// each service belongs to a company
public function company() {
return $this->belongsTo('Company');
}
public function history() {
return $this->morphMany('History', 'historable');
}
}
3) 路由
Route::get('/history', function(){
$histories = History::with('historable')->get();
return View::make('historyTemplate', compact('histories'));
});
4) 仅因为 $history->history->historable->company->name 记录了 n+1 的模板,将其注释掉,n+1 消失了.. 但我们需要远相关的公司名称:
@foreach($histories as $history)
<p>
<u>{{ $history->historable->company->name }}</u>
{{ $history->historable->name }}: {{ $history->historable->status }}
</p>
@endforeach
{{ dd(DB::getQueryLog()); }}
我需要能够快速加载公司名称(在单个查询中),因为它是多态关系模型 Product
和 Service
的相关模型.我已经为此工作了好几天,但找不到解决方案.History::with('historable.company')->get()
只是忽略了 historable.company
中的 company
.这个问题的有效解决方案是什么?
I need to be able to load the company names eagerly (in a single query) as it's a related model of the polymorphic relation models Product
and Service
.
I’ve been working on this for days but can't find a solution.
History::with('historable.company')->get()
just ignores the company
in historable.company
.
What would an efficient solution to this problem be?
推荐答案
解决方案:
这是可能的,如果您添加:
It is possible, if you add:
protected $with = ['company'];
到 Service
和 Product
模型.这样,每次加载 Service
或 Product
时都会预先加载 company
关系,包括通过与 Service
的多态关系加载时代码>历史代码>.
to both the Service
and Product
models. That way, the company
relation is eager-loaded every time a Service
or a Product
is loaded, including when loaded via the polymorphic relation with History
.
说明:
这将导致额外的 2 个查询,一个针对 Service
,一个针对 Product
,即每个 historable_type
一个查询.所以你的查询总数——不管结果的数量n
——来自m+1
(没有预先加载遥远的company
关系) 到 (m*2)+1
,其中 m
是由多态关系链接的模型数.
This will result in an additional 2 queries, one for Service
and one for Product
, i.e. one query for each historable_type
. So your total number of queries—regardless of the number of results n
—goes from m+1
(without eager-loading the distant company
relation) to (m*2)+1
, where m
is the number of models linked by your polymorphic relation.
可选:
这种方法的缺点是您将总是预先加载 Service
和 Product
上的 company
关系代码>模型.这可能是也可能不是问题,具体取决于您的数据的性质.如果这是一个问题,您可以使用此技巧在调用多态关系时自动预加载 company
only.
The downside of this approach is that you will always eager-load the company
relation on the Service
and Product
models. This may or may not be an issue, depending on the nature of your data. If this is a problem, you could use this trick to automatically eager-load company
only when calling the polymorphic relation.
将此添加到您的 History
模型中:
Add this to your History
model:
public function getHistorableTypeAttribute($value)
{
if (is_null($value)) return ($value);
return ($value.'WithCompany');
}
现在,当您加载 historable
多态关系时,Eloquent 将查找 ServiceWithCompany
和 ProductWithCompany
类,而不是 Service
或 Product
.然后,创建这些类,并在其中设置 with
:
Now, when you load the historable
polymorphic relation, Eloquent will look for the classes ServiceWithCompany
and ProductWithCompany
, rather than Service
or Product
. Then, create those classes, and set with
inside them:
ProductWithCompany.php
class ProductWithCompany extends Product {
protected $table = 'products';
protected $with = ['company'];
}
ServiceWithCompany.php
class ServiceWithCompany extends Service {
protected $table = 'services';
protected $with = ['company'];
}
...最后,您可以从基本的 Service
和 Product
类中删除 protected $with = ['company'];
.
...and finally, you can remove protected $with = ['company'];
from the base Service
and Product
classes.
有点老套,但应该可以.
A bit hacky, but it should work.
这篇关于Laravel - 热切加载多态关系的相关模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!