带有 Eloquent 过滤器的复杂查询
Complex query with filters with Eloquent
我有下表(和相关字段):
users (id, name)
companies (id, name)
sales (id, company_id, user_id)
products (id, description)
products_sales (id, product_id, sale_id)
现在,我生成了一些带有多个并发过滤器的报告:
公司名,用户名,产品。
我希望公司成为结果中的 'top level' 对象。
所以,
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 还是应该返回到 'raw' 查询?
您可以使用 getQueryLog
检查 Eloquent 生成的查询。如果您的查询正在查找 "less than optimal",您可以考虑使用原始查询。
如果您的查询是最优的,但 Eloquent 语法看起来太 "messy",您可以考虑将部分 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);
我有下表(和相关字段):
users (id, name)
companies (id, name)
sales (id, company_id, user_id)
products (id, description)
products_sales (id, product_id, sale_id)
现在,我生成了一些带有多个并发过滤器的报告: 公司名,用户名,产品。
我希望公司成为结果中的 'top level' 对象。 所以,
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 还是应该返回到 'raw' 查询?
您可以使用 getQueryLog
检查 Eloquent 生成的查询。如果您的查询正在查找 "less than optimal",您可以考虑使用原始查询。
如果您的查询是最优的,但 Eloquent 语法看起来太 "messy",您可以考虑将部分 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);