2015-05-14 55 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