mysql - Eloquent:如何连接一个直接相关的表和一个间接相关的表?

标签 mysql laravel eloquent laravel-5.2 laravel-5.3

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

  • 编号
  • 姓名
  • 有许多(项目)

订单:

  • 编号
  • 用户编号
  • 日期
  • 有许多(项目)

项目:

  • 编号
  • 产品编号
  • 订单编号
  • 属于(产品)
  • 属于(订单)

因此,每当客户购买某些产品时,就会创建 (Shopping) Items 记录。 Order(购物卡丁车)也被创建并且包含 1 个或多个 Items 属于一个客户(User)和他当前的购物过程。

现在这个问题是关于产品列表和分析它们“成功”的方法。它们多久售一次,最后一次售出是什么时候?

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

$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;
}

它返回上次购买该产品的日期,我可以在这样的 View 中使用它:

{{ $product->last_time_sold }}

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

是否有另一种方法可以在如上所述的数据库关系中获取产品的“last_time_sold”值,同时又不失去进行分页的能力?

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

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

最佳答案

将 LEFT JOIN 与 items 结合使用和 orders表格,分组依据 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: 如果您以严格模式(laravel 5.3 的默认模式)运行 MySQL,您将需要包括 products 中的所有列。 groupBy() 中的表条款。

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

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

关于mysql - Eloquent:如何连接一个直接相关的表和一个间接相关的表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39788360/

相关文章:

mysql - SQL 两个左连接和 CASE 语句

php - 基于 Laravel session 的身份验证 :api middleware not working

inheritance - Laravel Eloquent 事件继承

php - Laravel-5 和 Multi-Tenancy 数据库设置

mysql - Brighthouse group_concat 替代方案?

mysql - 如何使用hibernate交换数据库中的id

php - 如果数据长度可能超过 BLOB 大小,如何存储 CodeIgniter session 数据?

php - 语言切换器仅在 Artisan serve 运行时工作

php - 在php中获取ffmpeg中的音频持续时间

mysql - 使用 Laravel 选择倒数第二条记录 - Eloquent