Laravel - 渴望加载多态关系的相关模型 [英] Laravel - Eager Loading Polymorphic Relation's Related Models

查看:108
本文介绍了Laravel - 渴望加载多态关系的相关模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以加载多种关系/模型,没有任何n + 1个问题。但是,如果我尝试访问与多态模型相关的模型,则出现n + 1问题,我似乎找不到修复。这是在本地查看的确切设置:



1)数据库表名/数据



历史






公司






产品



< br>

服务







2)模型 / p>

  //历史
类历史延伸Eloquent {
protected $ table ='history';

public function historable(){
return $ this-> morphTo();
}
}

//公司
class公司扩展Eloquent {
protected $ table ='companies'

//每个公司都有很多产品
public function products(){
return $ this-> hasMany('Product');
}

//每个公司都有很多服务
public function services(){
return $ this-> hasMany('Service');
}
}

//产品
class产品扩展Eloquent {
//每个产品属于一个公司
public function company( ){
return $ this-> belongsTo('Company');
}

public function history(){
return $ this-> morphMany('History','historable');
}
}

//服务
类服务扩展Eloquent {
//每个服务属于一个公司
public function company ){
return $ this-> belongsTo('Company');
}

public function history(){
return $ this-> morphMany('History','historable');
}
}

3)路由 / p>

  Route :: get('/ history',function(){
$ historyories = historyable') - > get();
return View :: make('historyTemplate',compact('history));
});

4)具有n + 1日志记录的模板是由$ history-> historable->公司 - >名称,评论,n + 1离开..但我们需要这个遥远的相关公司名称:

  @foreach($ history作为$ history)
< p>
< u> {{$ history-> historable-> company-> name}}< / u>
{{$ history-> historable-> name}}:{{$ history-> historable-> status}}
< / p>
@endforeach
{{dd(DB :: getQueryLog());我需要能够快速加载公司名称(在单个查询中),因为它是多元关系模型 Product Service 的相关模型。
我一直在努力工作几天,但找不到解决方案。
历史:: with('historable.company') - > get()只是忽略公司 historable.company
这个问题的有效解决方案是什么?

解决方案

解决方案: p>

如果你添加,可以:

  protected $ with = ['company']; 

两个服务产品型号。这样,每次服务产品公司 / code>被加载,包括通过与 History 的多态关系加载时。






说明:



这将导致另外2个查询,一个用于服务,另一个用于 Product ,即每个 historable_type 。所以你的查询总数 - 不管结果的数量 n -goes from m + 1 (without eager-将远程公司关系)加载到(m * 2)+1 ,其中 m 是由多态关系链接的模型数。






可选:



这种方法的缺点是,您将永远在服务公司关系c>和产品型号。这可能是或可能不是一个问题,具体取决于您的数据的性质。如果这是一个问题,您可以使用此技巧在调用多态关系时自动加载公司



将此添加到您的历史模型中:

 {
if(is_null($ value))返回值($ value)
return($ value.'WithCompany');
}

现在,加载 historable 多态关系,Eloquent将寻找 ServiceWithCompany ProductWithCompany ,而不是服务产品。然后,创建这些类,并在其中设置



ProductWithCompany.php

  class ProductWithCompany extends Product {
protected $ table ='products';
protected $ with = ['company'];
}

ServiceWithCompany.php

  class ServiceWithCompany extends Service {
protected $ table ='services';
protected $ with = ['company'];
}

...最后,您可以删除 protected $ with = ['company']; 从基础服务产品类。



有点黑客,但它应该有效。


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) DB table name/data

history



companies



products



services



2) Models

// 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) Routing

Route::get('/history', function(){
    $histories = History::with('historable')->get();
    return View::make('historyTemplate', compact('histories'));
});

4) Template with n+1 logged only becacuse of $history->historable->company->name, comment it out, n+1 goes away.. but we need that distant related company name:

@foreach($histories as $history)
    <p>
        <u>{{ $history->historable->company->name }}</u>
        {{ $history->historable->name }}: {{ $history->historable->status }}
    </p>
@endforeach
{{ dd(DB::getQueryLog()); }}

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?

解决方案

Solution:

It is possible, if you add:

protected $with = ['company']; 

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.


Explanation:

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.


Optional:

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.

Add this to your History model:

public function getHistorableTypeAttribute($value)
{
    if (is_null($value)) return ($value); 
    return ($value.'WithCompany');
}

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'];
}

...and finally, you can remove protected $with = ['company']; from the base Service and Product classes.

A bit hacky, but it should work.

这篇关于Laravel - 渴望加载多态关系的相关模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆