2016-03-21 31 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