Laravel Eloquent 按关系表列排序

Posted

技术标签:

【中文标题】Laravel Eloquent 按关系表列排序【英文标题】:Laravel Eloquent sort by relation table column 【发布时间】:2014-06-25 03:41:08 【问题描述】:

我尝试通过pinned 列从shop_products_options 表中对shop_products 表中的产品进行排序:

$products = Shop\Product::with(['options' => function ($query) 

    $query->orderBy('pinned', 'desc'); 

])->paginate(5);

我在 Shop\Product 模型中设置了关系:

public function options()

    return $this->hasOne('Shop\Options');

但产品没有排序。我得到一个只适用于shop_products_options 表的查询。

SELECT * FROM `shop_products_options` WHERE `shop_products_options`.`product_id` in ('8', '9', '10', '11', '12') ORDER BY `pinned` DESC

如何解决?

【问题讨论】:

【参考方案1】:

急切加载使用单独的查询,因此您需要为此加入:

$products = Shop\Product::join('shop_products_options as po', 'po.product_id', '=', 'products.id')
   ->orderBy('po.pinned', 'desc')
   ->select('products.*')       // just to avoid fetching anything from joined table
   ->with('options')         // if you need options data anyway
   ->paginate(5);

SELECT 子句的存在是为了不将连接列附加到您的 Product 模型中。


编辑:根据@alexw 评论 - 如果需要,您仍然可以包含连接表中的列。您可以将它们添加到select 或致电addSelect/selectRaw 等。

【讨论】:

完美 - 此外,如果没有选择,shop_products_options.id 将覆盖 products.id 的值 @Gravy 没错,连接的表字段将覆盖主表中具有相同名称的所有字段。 感谢 Jarek,您帮助我解决了我遇到的类似问题,我需要按相关的 Broadcasts 对象(可能是 Youtube 或 Vimeo 视频)对 Broadcasts 进行排序我想按照他们获得的点赞数对他们进行排序。 另外值得注意的是,如果您有一个更复杂的查询,您可以使用addSelect 而不是select,并且您确实想要获取来自其他表的列. @alexw 当然,这是真的。然而问题是关于按关系排序,所以这里更相关的是如何避免意外行为,即包括(并且可能重叠)连接表中的列。【参考方案2】:

如果没有手动加入相关表,您将无法按相关表列排序。 Jarek 的回答是正确的,但这可能真的很尴尬:

1.第一个问题是你需要担心select。

->select('products.*')

原因:可以从 shop_products_options 中选择没有 select() id 并将其水合到 Product 模型中。

2.第二个问题是你需要担心groupBy。

->groupBy('products .id');

原因:如果关系是 HasOne 并且产品有多个 shop_products_options,则查询将返回更多产品行。

3.第三个问题是你需要改变所有其他的where子句:

->where('date', $date)

->where('products .date', $date)

原因:products 和 shop_products_options 都可以有 "date" 属性,在这种情况下,如果没有选择带有表 "ambiguous column" 的属性,则会抛出错误。

4.第四个问题是你使用的是表名(不是模型),这也很糟糕和尴尬。

->where('products.date', $date)

5.第五个问题是您需要担心连接表的软删除。如果 shop_products_options 使用 SoftDeletes 特征,您必须添加:

->where('shop_products_options .deleted_at', '=', null)

以上所有问题都非常令人沮丧,使用 eloquent 连接表格会给您的代码带来很多问题。我创建了一个包来解决上述所有问题,你可以用优雅的方式按关系属性排序,对于你的情况,它是:

$products = Shop\Product::orderByJoin('options.pinned', 'desc')->paginate(5);

欲了解更多信息,请参阅https://github.com/fico7489/laravel-eloquent-join

【讨论】:

【参考方案3】:

Subquery Ordering+6 Laravel 版本还有另一种方式:

$products = Shop\Product::orderByDesc(
        Shop\Options::select('pinned')
            ->whereColumn('product_id', 'shop_products.id')
            ->orderByDesc('pinned')
            ->limit(1)
)->paginate(5);

【讨论】:

哇!哇!谢谢@Ramy,你拯救了我的一周。我花了很多天才找到这个解决方案。再次感谢您的分享! 很高兴它有帮助!

以上是关于Laravel Eloquent 按关系表列排序的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Eloquent 按关系计数排序

Laravel Eloquent 模型按局部列和关系模型列值排序

Laravel按hasmany关系排序

如何对 Eloquent 关系中的数据透视表列进行 GROUP 和 SUM?

如何在 laravel 的 eloquent 中转换表列数据?

Laravel 6.0 Eloquent - 按日期和状态排序