Eloquent:如何加入一个直接相关的table和一个间接相关的table?
Eloquent: How to join a directly related table and a indirectly related table?
为了演示我的问题,我用这 4 个 table(定义为 Eloquent 模型)创建了一个虚拟数据库:User、Product, ShoppingItem (简称: Item), Order;与他们所有的相关关系。 (用户 包含在这里只是为了完整性)
产品:
- id
- 名字
- 有很多(项目)
订单:
- id
- user_id
- 日期
- 有很多(项目)
项目数:
- id
- product_id
- order_id
- 属于(产品)
- 属于(订单)
因此,每当客户购买某些产品时,就会创建(购物)商品 记录。还创建了 Order(购物卡丁车),其中包含 1 个或多个 Items,正好属于一个客户(User) 和他目前的购物流程。
现在这个问题是关于产品列表和分析它们的方法的'success'。它们多久出售一次,最后一次出售是什么时候?
例如,我可以使用以下查询获取单个产品的所有 订单,从而计算它们的销售频率:
$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;
}
它returns最后一次购买这个产品的日期,我可以在这样的视图中使用它:
{{ $product->last_time_sold }}
但是,我不能在 Eloquent OrderBy 语句中使用“last_time_sold”!
是否有另一种方法可以在如上所述的数据库关系中获取产品的“last_time_sold”的值而不失去进行分页的能力?
最后,查询产品 table 并在最后一次订购产品时订购产品的正确方法是什么?
请注意,商品没有日期,只有订单(购物卡丁车)有日期。
对 items
和 orders
table 使用 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
table 在 groupBy()
子句中。
注 3: 对于从未订购过的产品,last_ordered
列将为 NULL。在 MySQL 中,NULL 在使用 ASC
时排在第一位,在使用 DESC
时排在最后。但是据我所知,这没有记录在案。因此,如果您不想依赖该行为,请使用:
->orderbyRaw('max(orders.date) is null')
->orderBy('last_ordered', 'desc')
根据您执行此查询的频率,最好为产品的时间戳创建一个 "last_ordered" 列,并在每次有订单时触摸它。
为了演示我的问题,我用这 4 个 table(定义为 Eloquent 模型)创建了一个虚拟数据库:User、Product, ShoppingItem (简称: Item), Order;与他们所有的相关关系。 (用户 包含在这里只是为了完整性)
产品:
- id
- 名字
- 有很多(项目)
订单:
- id
- user_id
- 日期
- 有很多(项目)
项目数:
- id
- product_id
- order_id
- 属于(产品)
- 属于(订单)
因此,每当客户购买某些产品时,就会创建(购物)商品 记录。还创建了 Order(购物卡丁车),其中包含 1 个或多个 Items,正好属于一个客户(User) 和他目前的购物流程。
现在这个问题是关于产品列表和分析它们的方法的'success'。它们多久出售一次,最后一次出售是什么时候?
例如,我可以使用以下查询获取单个产品的所有 订单,从而计算它们的销售频率:
$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;
}
它returns最后一次购买这个产品的日期,我可以在这样的视图中使用它:
{{ $product->last_time_sold }}
但是,我不能在 Eloquent OrderBy 语句中使用“last_time_sold”!
是否有另一种方法可以在如上所述的数据库关系中获取产品的“last_time_sold”的值而不失去进行分页的能力?
最后,查询产品 table 并在最后一次订购产品时订购产品的正确方法是什么?
请注意,商品没有日期,只有订单(购物卡丁车)有日期。
对 items
和 orders
table 使用 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
table 在 groupBy()
子句中。
注 3: 对于从未订购过的产品,last_ordered
列将为 NULL。在 MySQL 中,NULL 在使用 ASC
时排在第一位,在使用 DESC
时排在最后。但是据我所知,这没有记录在案。因此,如果您不想依赖该行为,请使用:
->orderbyRaw('max(orders.date) is null')
->orderBy('last_ordered', 'desc')
根据您执行此查询的频率,最好为产品的时间戳创建一个 "last_ordered" 列,并在每次有订单时触摸它。