2015-05-14 97 views
1

我有一些關係(我希望可以正確解釋),並需要根據本質上是遠處的關係對輸出進行排序。Laravel渴望根據關係加載排序

我有一個數據透視表,其中包含許多關係的詳細信息,包括我想要排序的關係。

--User.php

public function players() 
{ 
    return $this->belongsToMany('App\Models\Player', 'league_player_user')->withPivot('position_id'); 
} 

--Player.php

public function position() 
{ 
    return $this->belongsToMany('App\Models\Position', 'league_player_user'); 
} 

我會急於加載像這樣的關係;

$user = User::with('players')->where('id', Auth::user()->id)->first(); 

所以,我想我想做這樣的事情(不起作用)。

$user = User::with(['players' => function($q){ 
    $q->orderBy('position.sort_id', 'asc'); 
}])->where('id', Auth::user()->id)->first(); 

關鍵表結構看起來有點像這樣;

league_player_user. 
...league_id 
...player_id 
...user_id 
...position_id 

的位置表包含sort_id

希望這是足夠的信息,如有需要,請要求更多。非常感謝。

+0

'User'模型中的'belongsToMany'會自動加入數據透視表(和遠表('players')),但不會加入'position'表(即使你說你想要position_id因此,你需要確保你自己添加額外的連接,我認爲你可以通過'User :: with('players','players.position')'做到這一點(儘管'position_id'是一個虛擬的你可能無法做到這一點,你必須使用代碼),重點在於'position'不會自動加入,所以你不能在它上面訂購。 – alexrussell

+0

感謝@alexrussell。我已經看過這個,是的 - 也渴望加載嵌套關係。然後我可以在players.position中使用orderBy()查詢,儘管它沒有錯誤,但它並沒有正確地對結果進行排序。 – Alex

+0

而不是使用' - > first()'use' - > toSql )'返回它正在生成的SQL - 這將幫助您準確調試ORM將要發佈的內容。 – alexrussell

回答

1

好吧,你試圖讓一個用戶,但與他們的球員(其中用戶bTM球員)已經填充和按位置(其中樞軸bT位置)排序。

在這種情況下,您將無法不經修改就使用Laravel的內置關係方法,因爲Laravel並非專門用於處理其他關係數據透視表上的關係。幸運的是,ORM具有足夠的靈活性,可以讓您通過「手動」連接完成所需的任務。

那麼直接回答你的問題,這裏的代碼,你需要的那種(你非常接近!):

$user = User::with(['players' => function ($q) { 
    $q->join('position', 'league_player_user.position_id', '=', 'position.id') 
     ->orderBy('position.sort_id', 'asc'); 
}])->where('id', Auth::user()->id)->first(); 

然而,在我看來,這不是偉大的代碼有以下幾個原因:

  1. 你手工獲得授權的用戶(當Auth::user()所以方便)
  2. 你實際上是不得不採取的具體實現邏輯從日(事實上​​,數據透視表被稱爲league_player_user,並把它...以及無論這是哪裏(您的控制器?)
  3. 這隻會影響這一個單一的查詢 - 如果你碰巧得到User某種其他方式(例如Auth::user()User::find(1)或其他),你不會有玩家正確

有序因此,我可能會建議,讓你的查詢更加簡單的目的,你不要急於負載的球員。因此,所有你需要做的前期是:

$user = Auth::user(); 

現在,(在你的User類球員()方法)的關係,你做的特殊訂貨工作:

public function players() 
{ 
    return $this->belongsToMany('App\Models\Player', 'league_player_user') 
       ->withPivot('position_id') 
       ->join('position', 'league_player_user.position_id', '=', 'position.id') 
       ->orderBy('position.sort_id'); 
} 

這樣,任何時候你打電話$user->players,你會得到他們正確的順序。

我必須補充一點,這樣做可能不會讓你急於加載玩家,因爲Laravel急於加載(即在ORM鏈中使用->with()),這是由於Laravel執行急切的加載查詢的方式 - 一個用於主要查詢(即用戶)和一個關係(即玩家),但它執行此查詢以獲取所有結果,因此可能無法與特殊訂購系統配合使用。你將不得不看看你是否真的在意急於加載玩家。在上面的例子中(你得到的是單個授權用戶),我認爲急切的加載並不重要。


編輯補充關於預先加載澄清:

我的背後暗示預先加載可能不工作的理由是,在Laravel渴望加載還挺喜歡這樣做:說你有類別和產品:Category HM ProductProduct bT Category。要獲得您使用的類別$category = Category::find(1),並且Laravel將其轉換爲查詢,如下所示:SELECT * FROM `categories` WHERE `id` = '1'。如果您隨後致電$category->products,Laravel將發出SELECT * FROM `products` WHERE `category_id` = '1'。這很明智。但是,如果你有下面的代碼它會更糟糕:

<?php $categories = Category::all(); ?> 

<ul> 
    @foreach ($categories as $category) 
     <li>Category {{{ $category->name }}} contains {{{ $category->products->count() }}} products.</li> 
    @endforeach 
</ul> 

在這種情況下,你有以下疑問:

SELECT * FROM `categories`; 
SELECT * FROM `products` WHERE `category_id` = '1'; 
SELECT * FROM `products` WHERE `category_id` = '2'; 
SELECT * FROM `products` WHERE `category_id` = '3'; 
... as many categories as you had 

但是,如果你要改變的是第一線,這:

<?php $categories = Category::with('products')->get(); ?> 

現在你只有兩個疑問:

SELECT * FROM `categories`; 
SELECT * FROM `products` WHERE `category_id` IN ('1', '2', '3', ...); 

然後,Laravel在調用第二個查詢之後,將根據它知道的類別ID爲您創建各種集合。

但是,這是簡單化的關係案例。在你的情況下,products()方法不僅僅是return $this->hasMany('Product');,但它包括一個數據透視表和一個手動連接等等,而且第二個查詢,即急切加載的查詢,可能無法應付並且做到這一點正確排序。

正如我所說,我不確定這是如何工作的,這對我來說只是一個紅旗。通過一切手段給它一個去看看你得到了什麼 - 你可能會發現它適合你。

+0

這完全是正確的答案。但是,您提到,如果不進行某些修改,以這種方式可能無法進行急切的加載。在這個特定的查詢中,會導致許多更多的DB查詢......這不是我應該擔心的事情嗎? (一頁最多30頁..) – Alex

+0

其實我認爲你在混合我說的兩件事(但是我的錯 - 我不是很清楚)。我說過,如果不做修改,你將無法做你想做的事。然後我給你這些修改。然後最後我建議用急切的加載來做這件事可能不行。我現在將我的推理添加到答案的底部,因爲它有點深入的解釋。但是最終我並不真正知道Laravel如何執行這種熱切的加載,因爲我只是在更簡單的情況下使用它,所以如果您想使用加載加載,請嘗試一下,看看它是否有效。 – alexrussell