如何在Eloquent中通过具有多对多关系的外部表中的列聚合进行分组?

时间:2019-06-25 14:29:34

标签: php sql laravel eloquent

我对此开始感到头疼,所以我认为我只是将其发布在这里。 我有两个通过数据透视表关联的表(因为这是多对多关系)。我使用Laravel和Eloquent(但也非常感谢您通过常规SQL查询获得有关如何实现此目的的一般帮助)。
我想基于第二个表的列来排序第一个表,但是为此需要将该列“聚合”。

许多驾驶员共享并具有不同颜色的汽车的示例:

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

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

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仅允许将表的列作为第二个参数。有没有其他方法可以从有序的联合查询中按正确的顺序获取模型?

2 个答案:

答案 0 :(得分:0)

这是我得到的最好成绩:

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

答案 1 :(得分:0)

您可以使用24 53 查询:

UNION

您如何订购具有相同$driversWithOnlyRedCars = Driver::select('*', DB::raw('0 as ordering')) ->whereDoesntHave('otherColoredCars'); $driversWithoutRedCars = Driver::select('*', DB::raw('1 as ordering')) ->whereDoesntHave('redCars'); $drivers = $driversWithOnlyRedCars->union($driversWithoutRedCars) ->orderBy('ordering') ->orderBy('') // TODO ->paginate(); 的驱动程序?您应该添加第二个ordering子句,以在每次执行查询时获得一致的顺序。