按关联列排序

43

我有以下查询:

$items = UserItems::with('item')
        ->where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->get();

我需要按照项目类型进行排序,因此我尝试了以下代码:

$items = UserItems::with('item')
        ->where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->orderBy('item.type')
        ->get();

但我收到了 在排序子句中未知的列'item.type'

我错过了什么?


1
你的表格叫做item还是items?我猜你的关系称为item,但表格名为items,所以应该是orderBy('items.type') - rypskar
我尝试了两种方法:Column not found: 1054 Unknown column 'items.type' in 'order clause' - TheUnreal
查看您的查询日志,您的主要查询和关系是相互独立的,它们不会交叉。您可能需要执行某种联接才能按另一个表排序。 - lagbox
2
可能是因为with()使用了急切加载,这将变成两个查询。尝试使用join()代替with() - rypskar
9个回答

65

多亏了@rypskar的评论,join()函数正常工作了。

$items = UserItems
        ::where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->join('items', 'items.id', '=', 'user_items.item_id')
        ->orderBy('items.type')
        ->select('user_items.*') //see PS:
        ->get();

注意:为了避免 id 属性(或任何两个表之间共享的名称属性)重叠并导致错误的值,您应该使用 select('user_items.*') 指定选择限制。


6
请注意,如果您希望将联接模型作为结果的一部分返回,仍可以像往常一样使用 with。在这个示例中,是 ->with('item') - Daniel Buckmaster
这将无法与形态关系一起使用 :( @Repox的答案更好 - Aleksandrs
@Aleksandrs,Repox的答案在分页上不起作用。 - Angus Simons

22
你可以使用withAggregate('relationship', 'column')函数。
它会为UserItems实例创建一个'item_type'属性,你可以通过orderBy('item_type')来对其进行排序。 它还可以与分页(如limit()、skip()等)一起使用,因为它在数据库调用后没有使用sortBy()。
UserItems::withAggregate('item','type')
    ->where('user_id','=',$this->id)
    ->where('quantity','>',0)
    ->orderBy('item_type')
    ->get();

这样,您仍然可以在优雅的查询中使用您的关联,并对集合进行排序,而无需进行连接或orderByRaw()调用。

这应该被接受的答案 - undefined

19

好的,你的急切加载可能没有构建你期望的查询,你可以通过启用查询日志来检查它。

但我可能会只使用一个集合过滤器:

$items = UserItems::where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->get()
        ->sortBy(function($useritem, $key) {
          return $useritem->item->type;
        });

由于某些原因,返回的数据是一个对象而不是像原始查询一样返回对象数组。 - TheUnreal
2
@TheUnreal 在调用 ->sortBy() 后需要调用 ->values()。 - rambii
3
按值进行排序的简写方式是 ->sortBy('item.type')。 - rambii
1
这将在内存中对获取的集合项进行排序。如果您想在数据库表上执行排序操作,应该在调用get()方法之前调用sortBy。 - Onur Demir
22
对于那些在分页时使用“跳过”和“限制”的人来说,这将导致错误的结果。 - molerat
显示剩余5条评论

6

我知道这是一个旧问题,但是你仍然可以使用"orderByRaw"而无需加入(join)。

$items = UserItems
        ::where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->orderByRaw('(SELECT type FROM items WHERE items.id = user_items.item_id)')
        ->get();  

2
对于一对多的关系,有一种更简单的方法。假设一个订单有多个付款,并且我们想按最新付款日期对订单进行排序。支付表具有一个名为order_id的外键字段。
我们可以像下面这样编写它:
$orders = Order->orderByDesc(Payment::select('payments.date')->whereColumn('payments.order_id', 'orders.id')->latest()->take(1))->get()

这段代码的SQL等效语句是什么?
select * from orders order by (
    select date
    from payments
    where order_id = payments.id
    order by date desc
    limit 1
) desc

你可以根据你的例子进行适应。如果我理解正确,你的情况下,“order”的等效词是“用户”,而“payment”的等效词是“项目”。
更多阅读资料:

https://reinink.ca/articles/ordering-database-queries-by-relationship-columns-in-laravel


1

你可以简单地通过这样做

UserItems::with('item')
  ->where('user_id','=',$this->id)
  ->where('quantity','>',0)
  ->orderBy(
      Item::select('type') 
      ->whereColumn('items.useritem_id','useritems.id')
      ->take(1),'desc'
    )    
  ->get();

0
$users
->whereRole($role)       
->join('address', 'users.id', '=', 'address.user_id')  
->orderByRaw("address.email $sortType")    
->select('users.*')

1
请记住,Stack Overflow 不仅旨在解决当前的问题,还要帮助未来的读者找到类似问题的解决方案,这需要理解底层代码。对于我们社区中的初学者和不熟悉语法的成员来说,这尤其重要。鉴于此,您能否编辑您的答案,包括您正在做什么的解释以及为什么您认为这是最好的方法?这在这里尤为重要,因为已经有几个经过社区验证的答案。 - Jeremy Caney

0
我发现了另一种使用相关模型字段对数据集进行排序的方法,您可以在模型中获取一个与相关表格唯一相关的函数(例如:房间表格与房间类别相关,并且房间通过类别ID与类别相关,您可以拥有一个名为“room_category”的函数,该函数基于Room Model的类别ID返回相关联的类别),然后代码将如下所示:
Room::with('room_category')->all()->sortBy('room_category.name',SORT_REGULAR,false);

这将按类别名称对房间进行排序。
我在一个数据表项目中使用了服务器端处理,遇到需要按相关实体的字段排序的情况,我像这样做,它可以工作。更容易,更符合MVC标准。
在您的情况下,也将以类似的方式进行:
User::with('item')->where('quantity','>',0)->get()->sortBy('item.type',SORT_REGULAR,false);

7
请注意,此操作将返回所有内容的集合,然后对其进行排序,如果您有大型数据集,则可能会出现问题。 - ctf0
此外,服务器端分页也无法工作,因为这种方法仅对已加载的项目进行排序(在分页的情况下,每页)。 - Dirk Jan

0
$items = UserItems::with('item')
        ->where('user_id','=',$this->id)
        ->where('quantity','>',0)
        ->with(['item' => function($q) {
          $q->orderBy('type', 'asc');
        ]})
        ->get();

将关系数组传递给“with”方法,其中:
  • 数组键是关系名称,在本例中为“item”
  • 数组值是一个闭包,它向贪婪加载查询添加附加约束条件

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接