Complex query with filters with Eloquent [closed]

六眼飞鱼酱① 提交于 2019-12-11 11:58:37

问题


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?


回答1:


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


来源:https://stackoverflow.com/questions/36132859/complex-query-with-filters-with-eloquent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!