复杂查询与过滤器与雄辩 [英] Complex query with filters with Eloquent

查看:106
本文介绍了复杂查询与过滤器与雄辩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下表格(和相关字段):

 用户(id,name)
公司id,name)
sales(id,company_id,user_id)
product(id,description)
products_sales(id,product_id,sale_id)
/ pre>

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



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

 公司:: 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]);
};
公司::其中('name','LIKE','%$ company_name%')
- > whereHas('sales',$ cond)
- > with([ 'sales'=> $ cond]);

我觉得这带来了不必要的重复。此外,当以相同的方式过滤
产品时,我认为热切的加载取代了之前的一个。



所以...最好的方法是什么?
我可以使用口述这个,还是应该回到原始查询?

解决方案

你可以检查Eloquent通过使用 getQueryLog 。如果您的查询看起来不太理想,则可以考虑原始查询。



如果您的查询是最佳的,但是优秀语法看起来太凌乱,您可以考虑构建一些部分查询范围到您的模型中。



例如,您可以定义一个范围在销售模型中匹配名称:

  class Sales extends Model 
{
/ **
*将查询范围仅包含具有匹配名称的用户的销售。
*
* @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用户名);
};

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

您甚至可以将 查询公司型号:

  class公司扩展模型
{
/ **
*将查询范围仅包括匹配用户和公司名称的公司和销售。
*
* @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);
}
}

然后你需要做的只是:

 公司:: matchCompanyAndUserName($ user_name,$ company_name); 


I have the following tables (and relevant fields):

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

Now, I have some report generation with multiple concurrent filters: company name, user name, product.

I want the Company to be the 'top level' object in the result. So,

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

then

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

This is complex enough already, and I also want to eager load all relationships, to optimize database access, so I ended up with:

$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]);

I feel this brings unnecessary repetition. Moreover, when filtering products the same way, I think the eager loading supersedes the previous one.

So... What's the better way to do this? Can I use Eloquent for this or should I go back to a 'raw' query?

解决方案

You can inspect the queries that Eloquent generates by using getQueryLog. If your query is looking "less than optimal", you might consider a raw query.

If your query is optimal but the Eloquent syntax looks too "messy", you might consider building some of the partial query scopes into your models.

For example, you could define a scope in your Sales model to match names:

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);
    }
}

Then your query becomes somewhat simpler (looking):

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

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

You can even package that query into your Company model:

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);
    }
}

Then all you need to do is:

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

这篇关于复杂查询与过滤器与雄辩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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