2015-05-14 7 views
1

У меня есть некоторые отношения (которые я могу надеяться объяснить правильно) и нужно сортировать вывод по тому, что по сути является далеким отношением.Laravel eager loading сортировать по отношениям

У меня сводная таблица, содержащая детали для многих отношений, в том числе то, что я хочу сортировать.

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

'ownToMany' в вашей модели User будет автоматически присоединяться к сводной таблице (и к далекой таблице (' игроки')), но не будет входить в таблицу 'position' (даже если вы говорите, что хотите' position_id 'column. Таким образом, вам нужно убедиться, что вы добавите дополнительное соединение самостоятельно. Я думаю, вы можете сделать это с помощью' User :: with ('players', 'players.position') '(хотя, поскольку' position_id' является виртуальным поле из опорных отношений, вы, возможно, не сможете это сделать, вам придется играть с кодом). Дело в том, что 'position' не присоединяется автоматически, поэтому вы не можете заказать на нем – alexrussell

+0

Спасибо @alexrussell.Я посмотрел на это, да, и я тоже хочу загрузить вложенное отношение. И тогда я могу запросить orderBy() в файле players.position, и хотя он не является ошибкой, он корректно не сортирует результаты. – 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 (т. Е. Используя ->with() в цепочке ORM) из-за того, что Laravel выполняет загружаемые запросы загрузки - один для основной запрос (т.е. пользователи) и один для отношений (т. е. игроков), но он делает этот запрос для получения всех результатов, поэтому, возможно, не будет работать с специальной системой заказа. Вам нужно будет увидеть, действительно ли вы волнуете загрузку игроков. В вашем случае выше (где вы получаете единственного авторизованного пользователя), на мой взгляд, интересная загрузка не так важна.


Редактировать, чтобы добавить разъяснения относительно нетерпеливого загрузки:

Мои рассуждения позади предполагая, что жадную загрузку не может работать, что жадная загрузка в Laravel делается вроде как это: у вас есть категории и продукты: Category Hm Product, Product 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 затем, после вызова второго запроса, создаст для вас различные коллекции на основе идентификаторов категорий, которые, как он знает, у вас есть.

Однако, это упрощенный случай отношений. В вашем случае метод products() не является только return $this->hasMany('Product');, но включает в себя свод и ручное соединение и т. Д., И вполне возможно, что этот второй запрос, который является загружаемым, просто не сможет справиться и сделать это упорядочивая правильно.

Как я уже сказал, я точно не знаю, как это работает, для меня это всего лишь красный флаг. Во что бы то ни стало дайте ему понять, что вы получаете - вы можете обнаружить, что он работает для вас.

+0

Это полностью правильный ответ. Однако вы отмечаете, что нетерпевая загрузка может быть невозможна таким образом, без каких-либо изменений. В этом конкретном запросе это привело бы к гораздо большему количеству запросов БД ... Разве это не то, о чем я должен беспокоиться? (до 30 для одной страницы ..) – Alex

+0

На самом деле, я думаю, вы смешиваете две вещи, которые я сказал (но по моей вине - я был не очень ясен). Я сказал, что вы не сможете делать то, что хотите, без изменений. Затем я даю вам эти изменения. Затем, в конце, я предлагаю, чтобы это с нетерпением загружалось, возможно, не работает. Теперь я добавлю свои рассуждения в нижнюю часть ответа, поскольку это немного подробное объяснение. Но, в конце концов, я действительно не знаю, как Laravel делает эту загружаемую загрузку, поскольку я использовал ее только в более упрощенном случае, поэтому, если вы хотите использовать активную загрузку, попробуйте ее и посмотрите, работает ли она. – alexrussell