2016-03-21 101 views
1

我有以下表格(及相关领域)滤波器复杂查询:与雄辩

users   (id, name) 
companies  (id, name) 
sales   (id, company_id, user_id) 
products  (id, description) 
products_sales (id, product_id, sale_id) 

现在,我有一些报表生成多个并发过滤器: 公司名称,用户名,产品。

我希望公司成为结果中的“顶级”对象。 所以,

Company::where('name', 'LIKE', "%$company_name%") 

然后

->whereHas('sales', function ($query) use ($user_name) { 
     $query->whereHas('user', function ($query2) use ($user_name) { 
      $query2->where('name', 'LIKE', "%$user_name%") 
     }); 
    }); 

这是复杂的已经够了,我也想跃跃欲试加载所有 关系,优化数据库访问,所以我结束了:

$cond = function($q) use ($user_name) { 
    $cond2 = function ($q2) use ($user_name) { 
     $q2->where('name', 'LIKE', "%$user_name%"); 
    }; 

    $q->whereHas('user', $cond2) 
     ->with(['user' => $cond2]); 
}; 
Company::where('name', 'LIKE', "%$company_name%") 
    ->whereHas('sales', $cond) 
    ->with(['sales' => $cond]); 

我觉得这会带来不必要的重复。而且,当以相同的方式过滤 产品时,我认为渴望加载会取代以前的加载。

那么...有什么更好的方法来做到这一点? 我可以使用Eloquent吗?还是应该回到'原始'查询?

回答

1

您可以使用getQueryLog检查Eloquent生成的查询。如果您的查询看起来“不够理想”,那么您可能会考虑使用原始查询。

如果您的查询是最优化的,但Eloquent语法看起来太“杂乱”,那么您可以考虑在模型中构建部分query scopes

例如,你可以在你的Sales型号相匹配的名称定义范围:

class Sales extends Model 
{ 
    /** 
    * Scope a query to only include Sales for users with a matching name. 
    * 
    * @return \Illuminate\Database\Eloquent\Builder 
    */ 
    public function scopeMatchUserName($query, $user_name) 
    { 
     $cond2 = function ($q2) use ($user_name) { 
      $q2->where('name', 'LIKE', "%$user_name%"); 
     }; 

     return $query->with(['user' => $cond2]) 
        ->whereHas('user', $cond2); 
    } 
} 

然后将查询变得简单一些(看):

$cond = function($q) use ($user_name) { 
    return $q->matchUserName($user_name); 
}; 

Company::where('name', 'LIKE', "%$company_name%") 
    ->with(['sales' => $cond]) 
    ->whereHas('sales', $cond); 

你甚至可以打包查询到您的Company型号:

class Company extends Model 
{ 
    /** 
    * Scope a query to only include Companies and Sales for a matching user and company name. 
    * 
    * @return \Illuminate\Database\Eloquent\Builder 
    */ 
    public function scopeMatchCompanyAndUserName($query, $user_name, $company_name) 
    { 
     $cond = function($q) use ($user_name) { 
      return $q->matchUserName($user_name); 
     }; 

     return $query->where('name', 'LIKE', "%$company_name%") 
      ->with(['sales' => $cond]) 
      ->whereHas('sales', $cond); 
    } 
} 

那么你所需要做的就是:

Company::matchCompanyAndUserName($user_name, $company_name); 
+0

谢谢!为getQueryLog提示+1,也为范围想法(可能是封装某些过滤逻辑的好地方)。然而,当我急于加载'with'时,'whereHas'没有必要的第一件事是不对的我想。 [详细资料在pastebin](http://pastebin.com/iyQBdq38) – alepeino

+0

嗯所以,'销售'没有任何匹配的用户应该从结果中完全省略? – alexw

+0

是的,在这种情况下,我只是想生成一个过滤每位用户销售额的报告。 – alepeino