【问题标题】:Group by multiple fields and then generate a sum按多个字段分组,然后生成总和
【发布时间】:2017-11-25 10:31:49
【问题描述】:

我有一个基于 Laravel 5.2 的食谱构建应用程序。

用户可以在他们的食谱中添加成分,有时您可以根据您使用该成分的目的添加两次相同的成分。

我正在构建允许用户为其食谱生成购物清单的功能,这应该为他们生成一个 PDF 购物清单。

到目前为止,我有正在生成的成分列表,但我无法完全理解如何对数量进行分组和求和。

典型的列表可能如下所示:

[  
   {  
      "id":97724,
      "name":"Citra Pellets",
      "amount":6.5,
      "hop_type_id":"Pellet",
      "recipe_id":23453
   },
   {  
      "id":97725,
      "name":"Simcoe Pellets",
      "amount":6.5,
      "hop_type_id":"Pellet",
      "recipe_id":23453
   },
   {  
      "id":97726,
      "name":"Citra Pellets",
      "amount":6.5,
      "hop_type_id":"Pellet",
      "recipe_id":23453
   },
   {  
      "id":97727,
      "name":"Simcoe Pellets",
      "amount":6.5,
      "hop_type_id":"Pellet",
      "recipe_id":23453
   },
   {  
      "id":97729,
      "name":"Mosaic Pellets",
      "amount":79.9999,
      "hop_type_id":"Pellet",
      "recipe_id":23453
   },
   {  
      "id":97730,
      "name":"Simcoe Pellets",
      "amount":60,
      "hop_type_id":"Combined",
      "recipe_id":23453
   }
]

目前,在我看来,我只有一个 foreach 循环遍历这些以吐出结果;结果是这样的:

+--------+----------------+------------+
| Amount |      Name      | Usage Type |
+--------+----------------+------------+
|    6.5 | Citra Pellets  | Pellet     |
|    6.5 | Simcoe Pellets | Pellet     |
|    6.5 | Citra Pellets  | Pellet     |
|    6.5 | Simcoe Pellets | Pellet     |
|  79.99 | Mosaic Pellets | Pellet     |
|     60 | Simcoe Pellets | Combined   |
+--------+----------------+------------+

我想做的是按成分的名称和使用类型对成分进行分组,然后得到数量的总和。所以输出是这样的:

+--------+----------------+------------+
| Amount |      Name      | Usage Type |
+--------+----------------+------------+
|     13 | Citra Pellets  | Pellet     |
|     13 | Simcoe Pellets | Pellet     |
|  79.99 | Mosaic Pellets | Pellet     |
|     60 | Simcoe Pellets | Combined   |
+--------+----------------+------------+

到目前为止,我的尝试包括App\Models\Recipe::find(23453)->hops->groupBy('name') - 这给了我如下内容:

{  
   "Citra Pellets":[  
      {  
         "id":97724,
         "name":"Citra Pellets",
         "amount":6.5,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      },
      {  
         "id":97726,
         "name":"Citra Pellets",
         "amount":6.5,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      },
      {  
         "id":97728,
         "name":"Citra Pellets",
         "amount":94.9999,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      }
   ],
   "Simcoe Pellets":[  
      {  
         "id":97725,
         "name":"Simcoe Pellets",
         "amount":6.5,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      },
      {  
         "id":97727,
         "name":"Simcoe Pellets",
         "amount":6.5,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      },
      {  
         "id":97730,
         "name":"Simcoe Pellets",
         "amount":60,
         "hop_type_id":"Combined,
         "recipe_id":23453
      }
   ],
   "Mosaic Pellets":[  
      {  
         "id":97729,
         "name":"Mosaic Pellets",
         "amount":79.9999,
         "hop_type_id":"Pellet",
         "recipe_id":23453
      }
   ]
}

这允许我循环遍历每个然后->sum('amount') 以获得总和值。但是它没有考虑到重要的hop_type_id

我也尝试过链接->groupBy('hop_type_id'),以及使用->each(function ($item, $key)) 的变体,但还没有设法解决这个问题。

看起来集合上的初始 groupBy() 生成了一个键对象,其下方有一个值数组。

理想情况下,我所追求的是每个跃点名称下每个 hop_type_ids 的另一个级别,然后我可以使用它来生成每个跃点 namehop_type_id 的总和。

我怎样才能做到这一点?

【问题讨论】:

    标签: php arrays laravel collections laravel-5.2


    【解决方案1】:

    我想你正在寻找这个-

    \App\Ingredient::where('recipe_id','=',23453)->selectRaw('sum(amount) as amount, name, hop_type_id')->groupBy('name')->groupby('hop_type_id')->get();
    

    希望对你有帮助:)

    【讨论】:

    • 我会试一试,但很想用现有的查询来实现这一点。
    • @James 有时需要逐部分分解,如果有帮助,请告诉我,我会尝试使用您现有的查询。
    【解决方案2】:

    您可以将整个接收器重写为一系列成分。这样 Recept 对象的类型将是

         Ingredient[] 
    

    每种成分都有以下私有字段和相应的公共获取器

             $id, (string) $name, (int) $amount, $hop_type_id, $recipe_id
    

    然后您可以生成如下购物清单

    $shoppingList = [];
    foreach($recept as $ingredient) {
        $shoppingList[$ingredient->getName()] += $ingredient->getAmount();
    }
    

    【讨论】:

    • 这是现有应用程序的新功能,遗憾的是没有机会重写。
    • 如果在 foreach 循环的结果中您只是检查一种成分是否已添加到列表中,该怎么办?如果是,您只需添加新数量,在另一种情况下,您将新元素添加到列表中。如果你发布你的 foreach 循环,我可以更具体。
    【解决方案3】:

    所以我使用现有集合解决了它,而无需使用原始查询或修改数据库架构。

    您可以使用groupBy() 然后transform() 来实现:

    $recipe = App\Models\Recipe::find(23453);
    
    $hops = $recipe->hops->groupBy('name')
            ->transform(function ($hop, $key) {
                return $hop->groupBy('hop_usage_type');
            });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多