Eloquent:如何加入直接相关的表和间接相关的表?

时间:2016-09-30 09:38:28

标签: mysql laravel eloquent laravel-5.2 laravel-5.3

为了演示我的问题,我创建了一个虚拟数据库,包含这4个表(定义为Eloquent模型):用户产品,ShoppingItem(简称:项目),订单;与他们所有相关的关系。 (用户仅为完整性而包含在此处)
产品:

  • ID
  • 姓名
  • 的hasMany(项目)

订单

  • ID
  • USER_ID
  • 日期
  • 的hasMany(项目)

产品:

  • ID
  • PRODUCT_ID
  • ORDER_ID
  • 属于关联(产品)
  • 属于关联(订单)

因此,只要客户购买某些产品,就会创建(购物)商品记录。还创建了订单(购物卡丁车),其中包含一个或多个属于一个客户的用户)和他当前的购物过程

现在这个问题是关于产品清单和分析其“成功”的方法。它们的销售频率和最后一次销售的时间是什么时候?

例如,我可以使用以下查询获取单个产品的所有订单,从而计算它们的销售频率:

$orders = Order::whereHas('items', function ($query) use ($id) {
    $query->where('product_id', $id) 
          ->where('date', '<', Carbon::now());
})->orderBy('date', 'desc')->get();

此外,我可以通过以下方式获得所售产品的清单:

$products = Products::withCount('items')
                    ->orderBy('item_count', 'desc');

但我希望得到所有产品的清单,按照上次订购(购买)的日期排序。该结果必须是Eloquent查询或查询生成器集,以便我可以使用分页。

我的产品型号中定义了一个Mutator,如下所示:

public function getLastTimeSoldAttribute( $value ) {

    $id = $this->id;

    // get list of plans using this song
    $order = Order::whereHas('items', function ($query) use ($id) {
        $query->where('product_id', $id) 
              ->where('date', '<', Carbon::now());
    })->orderBy('date', 'desc')->first();

    return $order->date;
}

它返回上次购买此产品的日期,我可以在以下视图中使用它:

{{ $product->last_time_sold }}

但是,我不能在Eloquent OrderBy语句中使用' last_time_sold '!

是否有另一种方法可以在如上所述的数据库关系中获取产品的' last_time_sold ',而不会失去分页的能力?

最终,查询Products表并在上次订购产品时订购产品的正确方法是什么?

请注意,物品没有日期,只有订单(购物卡丁车)有日期。

2 个答案:

答案 0 :(得分:1)

itemsorders表使用LEFT JOIN,products分组,MAX(orders.date)分组。

$products = Product::leftJoin('items', 'items.product_id', '=', 'products.id')
    ->leftJoin('orders', 'orders.id', '=', 'items.order_id')
    ->groupBy('products.id')
    ->selectRaw('products.*, max(orders.date) as last_ordered')
    ->orderBy('last_ordered', 'desc')
    ->get();

这将执行以下查询:

select products.*, max(orders.date) as last_ordered
from `products`
left join `items` on `items`.`product_id` = `products`.`id`
left join `orders` on `orders`.`id` = `items`.`order_id`
group by `products`.`id`
order by `last_ordered` desc

注1:如果您只想获取已订购的产品,请使用join()代替leftJoin

注意2:如果使用严格模式运行MySQL(laravel 5.3的默认值),则需要在products子句中包含groupBy()表中的所有列。< / p>

注3:对于从未订购的产品,last_ordered列将为NULL。在MySQL中,NULL在使用ASC时首先排序,在使用DESC时排在最后。但AFAIK没有记录。因此,如果您不想依赖该行为,请使用:

->orderbyRaw('max(orders.date) is null')
->orderBy('last_ordered', 'desc')

答案 1 :(得分:0)

根据您执行此查询的频率,最好为产品上的时间戳创建“last_ordered”列,并在每次订单时触摸它。