首页 > 解决方案 > 如何通过 Eloquent 中具有多对多关系的外部表中的列聚合进行分组?

问题描述

我开始为此头疼,所以我想我只是在这里发布。我有两个通过数据透视表关联的表(因为它是多对多关系)。我使用 Laravel 和 Eloquent(但对于如何使用普通 SQL 查询实现这一点的一般帮助也非常感谢)。
我想根据第二个表的列订购第一个表,但为此需要“聚合”列。

由许多司机共享并且可以有不同颜色的汽车的示例:

Car-Table: [id, color]  
Driver-Table: [id, name]  
Car.Driver-Table: [car_id, driver_id]

我需要一个查询来获取所有驾驶红色汽车的司机,然后是所有驾驶红色汽车的司机。
我必须使用查询,因为之后我可能会对此查询执行其他操作(如过滤)并希望最后分页。
我已经使用了获取两个组之一的查询。它们看起来像这样: 在 Driver 模型中:

public function redCars() {
    return $this->cars()->where('color', 'red');
}

public function otherColoredCars() {
    return $this->cars()->where('color', '<>', 'red');
}

然后在控制器的某个地方:

$driversWithOnlyRedCars = Driver::whereDoesntHave('otherColoredCars')->get();
$driversWithoutRedCars = Driver::whereDoesntHave('redCars')->get();

有没有办法将这两者结合起来?
也许我在这里的想法完全错误。

更新澄清:
基本上我需要这样的东西(或任何其他会导致相同结果的方式)

$driversWithOnlyRedCars->addTemporaryColumn('order_column', 0); // Create temporary column with value 0
$driversWithoutRedCars->addTemporaryColumn('order_column', 1);
$combinedQuery = $driversWithOnlyRedCars->combineWith($driversWithoutRedCars); // Somehow combine both queries
$orderedQuery = $combinedQuery->orderBy('order_colum');
$results = $combinedQuery->get();

更新 2
我想,我发现了如何通过原始查询接近我的目标。
会是这样的:

$a = DB::table(DB::raw("(
  SELECT id, 0 as ordering
  FROM drivers
  WHERE EXISTS (
    SELECT * FROM cars
    LEFT JOIN driver_car ON car.id = driver_car.car_id
    WHERE driver.id = driver_car.driver_id
    AND cars.color = 'red'
  )
) as only_red_cars"));

$b = DB::table(DB::raw("(
  SELECT id, 1 as ordering
  FROM drivers
  WHERE EXISTS (
    SELECT * FROM cars
    LEFT JOIN driver_car ON car.id = driver_car.car_id
    WHERE driver.id = driver_car.driver_id
    AND cars.color <> 'red'
  )
) as no_red_cars"));

$orderedQuery = $a->union($b)->orderBy('ordering');

现在的问题是我需要像这样订购并最终分页的模型,所以这并不是我问题的真正答案。我试图将其转换回模型,但我还没有成功。我尝试了什么:

$queriedIds = array_column($orderedQuery->get()->toArray(), 'id');
$orderedModels = Driver::orderByRaw('(FIND_IN_SET(drivers.id, "' .  implode(',', $queriedIds) . '"))');

但看起来FIND_IN_SET只允许表格的一列作为第二个参数。是否有另一种方法可以从有序联合查询中以正确的顺序获取模型?

标签: phpsqllaraveleloquent

解决方案


这是我得到的最好的:

$driversWithOnlyRedCars = Driver::whereHas('cars',function($q){
  $q->where('color', 'red');
})->withCount('cars')->get()->where('cars_count',1);

推荐阅读