在拉拉维尔的急切装载中,从多对多的关系中获取第一个或最新的第一个

时间:2018-01-22 07:36:02

标签: php laravel laravel-5.4 laravel-eloquent laravel-collection

我正在laravel 5.4中构建一个小应用程序,我有两个模型ContactCompanies我之间有多对多的关系,在我的的 Contact Model:

public function company()
{
    return $this
        ->belongsToMany('App\Company', 'company_contact','contact_id', 'company_id')->withTimestamps();
}

现在在某个地方,我想拥有现有的公司,即我希望latest() first()。或orderBycreated_by desc并获取first()行。为此,我必须做这样的事情:

$contacts = Contact::where('name', 'LIKE', '%'. $request->search_input. '%')
    ->orWhere('email', 'LIKE', '%'. $request->search_input. '%')
    ->orWhereHas('company', function ($q) use($request) {
        $q->where('name', 'LIKE', '%'. $request->search_input. '%');
    })
    ->with('company')
    ->orderBy('created_at', 'desc')
    ->paginate(50);
foreach ($contacts as $contact)
{
    $contact->company = $contact->company()->withPivot('created_at')->orderBy('pivot_created_at', 'desc')->first();
}

要删除foreach,我尝试在Contact模型中使用新关系:

public function currentCompany()
{
    return $this
        ->belongsToMany('App\Company', 'company_contact','contact_id', 'company_id')
        ->withTimestamps()
        ->orderBy('created_at', 'desc')
        ->first();
}

但是在控制器中取物时:

$contacts = Contact::where('name', 'LIKE', '%'. $request->search_input. '%')
    ->orWhere('email', 'LIKE', '%'. $request->search_input. '%')
    ->orWhereHas('currentCompany', function ($q) use($request) {
        $q->where('name', 'LIKE', '%'. $request->search_input. '%');
    })
    ->with('CurrentCompany')
    ->orderBy('created_at', 'desc')
    ->paginate(50);

但它让我犯了错误,是否有任何eloquent方式或Collection方式删除此foreach

2 个答案:

答案 0 :(得分:1)

在封闭内使用first() -

$contacts = Contact::where('name', 'LIKE', '%'. $request->search_input. '%')
->orWhere('email', 'LIKE', '%'. $request->search_input. '%')
->with(['company'=>function ($q) use($request) {
    $q->where('name', 'LIKE', '%'. $request->search_input. '%')->first();
}])
->orderBy('created_at', 'desc')
->paginate(50);

或者像这样 -

$contacts = Contact::where('name', 'LIKE', '%'. $request->search_input. '%')
->orWhere('email', 'LIKE', '%'. $request->search_input. '%')
->orWhereHas('company', function ($q) use($request) {
    $q->where('name', 'LIKE', '%'. $request->search_input. '%');
})
->with(['company'=>function($q){
       $q->first();  
}])
->orderBy('created_at', 'desc')
->paginate(50);

这样您就不需要进行任何additional foreach循环。

答案 1 :(得分:1)

你无法真正做到你想要直接做的事情。关系是查询快捷方式。您可以确保只获得第一个,但如果您的关系是一个/多个,那么它仍将是一个集合的一部分。

考虑一下:

$contacts = Contact::where('name', 'LIKE', '%'. $request->search_input. '%')
    ->orWhere('email', 'LIKE', '%'. $request->search_input. '%')
    ->orWhereHas('company', function ($q) use($request) {
        $q->where('name', 'LIKE', '%'. $request->search_input. '%');
    })
    ->with([ 'company' => function ($query) {
            return $query->latest(); //Take the latest only 
    })
    ->orderBy('created_at', 'desc')
    ->paginate(50);

但要访问公司,您需要执行$contacts[index]->company->first()之类的操作,因为它甚至会产生一系列相关公司。

问题是ORM将执行2个查询,第一个将检索完成查询的所有Contact模型,第二个将检索所检索的所有联系人的所有相关模型。这意味着如果您执行将其限制为1个相关结果的操作,则最终只能获取所有检索到的联系人。