【问题标题】:Laravel Eloquent sort by relation table columnLaravel Eloquent 按关系表列排序
【发布时间】: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

如何解决?

【问题讨论】:

    标签: php orm laravel eloquent


    【解决方案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,你拯救了我的一周。我花了很多天才找到这个解决方案。再次感谢您的分享!
      • 很高兴它有帮助!
      猜你喜欢
      • 2016-06-21
      • 2021-01-25
      • 2021-07-14
      • 2017-08-22
      • 2015-07-05
      • 2016-10-15
      • 1970-01-01
      • 1970-01-01
      • 2020-01-08
      相关资源
      最近更新 更多