Won*_*nka 28 polymorphism polymorphic-associations eager-loading laravel eloquent
我可以在没有任何n + 1问题的情况下急切加载多态关系/模型.但是,如果我尝试访问与多态模型相关的模型,则会出现n + 1问题,而我似乎无法找到修复.以下是在本地查看的确切设置:
1)DB表名/数据
history
companies
products
services
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');
}
}
Run Code Online (Sandbox Code Playgroud)
3)路由
Route::get('/history', function(){
$histories = History::with('historable')->get();
return View::make('historyTemplate', compact('histories'));
});
Run Code Online (Sandbox Code Playgroud)
4)只有$ 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()); }}
Run Code Online (Sandbox Code Playgroud)
我需要能够急切地加载公司名称(在单个查询),因为它是多态的关系模型的相关模型Product
和Service
.我已经工作了好几天但找不到解决方案.
History::with('historable.company')->get()
只是忽略了company
in historable.company
.这个问题的有效解决方案是什么?
dam*_*ani 58
解:
如果你添加:
protected $with = ['company'];
Run Code Online (Sandbox Code Playgroud)
同时Service
和Product
模特.这样,company
每次加载Service
或加载时都会急切地加载关系Product
,包括通过多态关系加载时History
.
说明:
这将导致额外的2个查询,一个用于Service
一个查询,一个用于Product
查询,即每个查询一个查询historable_type
.所以你的查询总数 - 不管结果的数量 - n
来自m+1
(没有急切加载远距离company
关系)(m*2)+1
,其中m
是你的多态关系链接的模型数量.
可选的:
这种方法的缺点是你总是急于加载和模型company
上的关系.这可能是也可能不是问题,具体取决于数据的性质.如果这是一个问题,您可以使用此技巧仅在调用多态关系时自动加载.Service
Product
company
将此添加到您的History
模型:
public function getHistorableTypeAttribute($value)
{
if (is_null($value)) return ($value);
return ($value.'WithCompany');
}
Run Code Online (Sandbox Code Playgroud)
现在,当你加载historable
多态关系,雄辩会寻找类ServiceWithCompany
和ProductWithCompany
,而不是Service
或Product
.然后,创建这些类,并with
在其中设置:
ProductWithCompany.php
class ProductWithCompany extends Product {
protected $table = 'products';
protected $with = ['company'];
}
Run Code Online (Sandbox Code Playgroud)
ServiceWithCompany.php
class ServiceWithCompany extends Service {
protected $table = 'services';
protected $with = ['company'];
}
Run Code Online (Sandbox Code Playgroud)
...最后,您可以protected $with = ['company'];
从基础Service
和Product
类中删除.
有点hacky,但它应该工作.
Raz*_*zor 10
你可以分开集合,然后懒得加载每一个:
$histories = History::with('historable')->get();
$productCollection = new Illuminate\Database\Eloquent\Collection();
$serviceCollection = new Illuminate\Database\Eloquent\Collection();
foreach($histories as $history){
if($history->historable instanceof Product)
$productCollection->add($history->historable);
if($history->historable instanceof Service)
$serviceCollection->add($history->historable);
}
$productCollection->load('company');
$serviceCollection->load('company');
// then merge the two collection if you like
foreach ($serviceCollection as $service) {
$productCollection->push($service);
}
$results = $productCollection;
Run Code Online (Sandbox Code Playgroud)
可能它不是最好的解决方案,protected $with = ['company'];
按照@damiani的建议添加是一个很好的解决方案,但这取决于您的业务逻辑.
只需更新您的 Laravel 版本和以下代码
protected $with = [‘likeable.owner’];
Run Code Online (Sandbox Code Playgroud)
将按预期工作。