按两列分组,然后使用 Laravel Eloquent 将每组中的另一列相加

问题描述 投票:0回答:2

使用 Laravel 查询两个表后,我尝试按两列对结果进行分组,并对每组中的第三列进行求和。

这是一个带有示例数据的最小模式来支持我的问题:

CREATE TABLE plan (id int, user_id int, game_id int, amount int, `option` varchar(100));
INSERT INTO plan (id, user_id, game_id, amount, `option`)
VALUES
(1, 1, 6, 10, 'option1'),
(2, 1, 6, 12, 'option1'),
(3, 2, 6, 10, 'option1'),
(4, 2, 6, 12, 'option1'),
(5, 2, 6, 5, 'option2'),
(6, 2, 6, 6, 'option2');

CREATE TABLE users (id int, name varchar(100));
INSERT INTO users (id, name)
VALUES
(1, 'username1'),
(2, 'username2');

我当前的代码:(PHPize 演示)

$game = (object) ['id' => 6];

var_export(
    $db::table('plan')
    ->select('plan.amount', 'plan.option', 'users.name')
    ->join('users', 'plan.user_id', '=', 'users.id')
    ->where(['plan.game_id' => $game->id])
    ->get()
    ->groupBy('name')
    ->map(function ($option) {
        return $option
            ->groupBy('option');
    })
    ->toArray()
);

目前结果:

array (
  'username1' => 
  array (
    'option1' => 
    array (
      0 => 
      (object) array(
         'amount' => 12,
         'option' => 'option1',
         'name' => 'username1',
      ),
      1 => 
      (object) array(
         'amount' => 10,
         'option' => 'option1',
         'name' => 'username1',
      ),
    ),
  ),
  'username2' => 
  array (
    'option2' => 
    array (
      0 => 
      (object) array(
         'amount' => 6,
         'option' => 'option2',
         'name' => 'username2',
      ),
      1 => 
      (object) array(
         'amount' => 5,
         'option' => 'option2',
         'name' => 'username2',
      ),
    ),
    'option1' => 
    array (
      0 => 
      (object) array(
         'amount' => 12,
         'option' => 'option1',
         'name' => 'username2',
      ),
      1 => 
      (object) array(
         'amount' => 10,
         'option' => 'option1',
         'name' => 'username2',
      ),
    ),
  ),

我需要将每个二级“选项”组的“金额”相加。

我需要这个简化的结果结构:

array (
  'username1' => 
  array (
    'option1' => 22
  ),
  'username2' => 
  array (
    'option2' => 11
    'option1' => 22
  ),
php laravel group-by eloquent sum
2个回答
1
投票

您可以使用laravel

reduce()
方法https://laravel.com/docs/5.7/collections#method-reduce.

在你的地图函数中,在 group by 之后添加reduce,如下所示:

->map(function ($option) {
    return $option
        ->groupBy('option')
        ->map(function($group) {
           // Loop through each group and reduce them.
           $group->reduce(function($carry, $item) {
                // Assume that we always want the last value by using php end() function on array.
                return $carry + end($item);
           }, 0);
        });

});

我还没有测试过这个,所以如果有问题请告诉我,我会更新答案。


0
投票

虽然链接嵌套方法负载来尝试构建“纯 Laravel”解决方案可能很可爱,但最好在数据库级别进行分组和求和并跳过所有多余的映射和分组调用;只需使用

reduce()
进行分组和求和即可。

代码:(PHPize演示

var_export(
    $db::table('plan')
    ->selectRaw('users.name, plan.option, SUM(plan.amount) amount')
    ->join('users', 'plan.user_id', '=', 'users.id')
    ->where(['plan.game_id' => $game->id])
    ->groupBy('name', 'option')
    ->get()
    ->reduce(
        function($result, $row) {
            $result[$row->name][$row->option] = ($result[$row->name][$row->option] ?? 0) + $row->amount;
            return $result;
        }
    )
);

输出:

array (
  'username1' => 
  array (
    'option1' => 22,
  ),
  'username2' => 
  array (
    'option2' => 11,
    'option1' => 22,
  ),
)

如果您对方法链接的重视程度高于时间复杂度,那么以“sym() 调用为目标的列结尾的一对

groupBy()
map()
调用将提供与上面相同的关联 2d 结果。 (PHPize 演示)

var_export(
    $db::table('plan')
    ->select('plan.amount', 'plan.option', 'users.name')
    ->join('users', 'plan.user_id', '=', 'users.id')
    ->where(['plan.game_id' => $game->id])
    ->get()
    ->groupBy('name')
    ->map(
        fn($names) => $names
            ->groupBy('option')
            ->map(
                fn($options) => $options->sum('amount')
            )
    )
    ->toArray()
);
© www.soinside.com 2019 - 2024. All rights reserved.