両方(UNION等で結合が要)対応する場合

前回で実績のみ対応した。([誤差を求め、特定の値(filter)を取得する](https://qiita.com/kenji123/items/e1359168b42d58af7cae))
今回は両方(実績、予算)対応する。
その場合であるが、グループ化を３つの変数に対し行うことになりネストが深くなる。
今回はコレクション型でforeachでも扱えるのでそれを使用する。

### モデル
[LaravelでDBファザードのDB::selectで対応](https://qiita.com/kenji123/items/84c90f1339a81a932180)と同じ。

### コントローラ

```ProjectWorkController.php
<?php

namespace App\Http\Controllers;

use App\Models\Models\ProjectWork as ModelsProjectWork;
use Illuminate\Http\Request;
use App\Models\Models\ProjectWork;
use Illuminate\Support\Facades\DB;

class ProjectWorkController extends Controller
{
    public function index()
    {

        $results = ProjectWork::getWorkHoursPerProjectInAMonth('2023-06-01', '2023-08-31');

        $results = collect($results)->groupBy(['employee_id', 'month', 'budget_actual_flag']);
        foreach ($results as $employeeId => $months) {
            foreach ($months as $month => $flags) {
                foreach ($flags as $flag => $data) {
                    $totalRatio = 0;

                    // 各 budget_actual_flag の値（0または1）について、ratio の合計を計算します。
                    foreach ($data as $datum) {
                        $totalRatio += $datum->ratio;
                    }

                    echo "Before adjustment, total ratio for employee {$employeeId}, month {$month}, flag {$flag}: " . number_format($totalRatio, 2) . "\n";

                    // 各 budget_actual_flag の値（0または1）について、ratio を調整します。
                    foreach ($data as $index => $datum) {
                        // Round to 2 decimal places after calculating the new ratio
                        $datum->ratio = round($datum->ratio / $totalRatio, 2);
                        $data[$index] = $datum;
                    }

                    $totalRatio = 0;
                    foreach ($data as $datum) {
                        $totalRatio += $datum->ratio;
                    }

                    if ($totalRatio !== 1.0) {
                        $data[count($data) - 1]->ratio += round(1 - $totalRatio, 2);
                    }

                    $totalRatio = 0;
                    foreach ($data as $datum) {
                        $totalRatio += $datum->ratio;
                    }

                    echo "After adjustment, total ratio for employee {$employeeId}, month {$month}, flag {$flag}: " . number_format($totalRatio, 2) . "\n";

                    $flags[$flag] = $data;  // 参照を使用せずにデータを直接変更
                }
                $months[$month] = $flags;  // 参照を使用せずにデータを直接変更
            }
            $results[$employeeId] = $months;  // 参照を使用せずにデータを直接変更
        }


        dd($results);
    }
}
```

`collect($results)->groupBy(['employee_id', 'month', 'budget_actual_flag'])`
にて、'employee_id', 'month', 'budget_actual_flag'をグループ化し、比率を再計算することになる。
その操作をする場合、mapメソッドでratioにアクセスすることになるが、ネストが深くなり可読性が悪くなる。
よって、今回はコレクション型をPHPの連想配列のようにforeachで処理している。
コレクション型のmapメソッドを使えば新しいコレクションを返すが、今回は直接データを書き換えて対応している。(ただ、今回はオブジェクトのプロパティにアクセスしているので直接$resultを書き換えており、`$flags[$flag] = $data`等３つの記載は冗長になる。)

(結果例)
```php
Illuminate\Support\Collection {#654 ▼ // app/Http/Controllers/ProjectWorkController.php:435
  #items: array:22 [▶
    "EMP001" => Illuminate\Support\Collection {#662 ▶
      #items: array:3 [▶
        202306 => Illuminate\Support\Collection {#666 ▶
          #items: array:2 [▶
            0 => Illuminate\Support\Collection {#664 ▶
              #items: array:4 [▶
                0 => {#297 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ001"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "8.00"
                  +"ratio": 0.33
                }
                1 => {#299 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ002"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "7.00"
                  +"ratio": 0.29
                }
                2 => {#300 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ003"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "7.00"
                  +"ratio": 0.29
                }
                3 => {#301 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ004"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "2.00"
                  +"ratio": 0.09
                }
              ]
```


### 特定プロジェクトのみ取得
先ほどに対し、以下コードを追加。
ネストが深くなるが今回はmapメソッドを使用した。
対象データがない場合、何もない配列ができるので、isNotEmpty()で返している。
```php
        $filterResults = $results->map(function ($months) {
            return $months->map(function ($flags) {
                return $flags->map(function ($data) {
                    return $data->filter(function ($data_s) {
                        return $data_s->project_code == 'PRJ0001';
                    });
                })->filter(function ($data) {
                    return $data->isNotEmpty();
                });
            })->filter(function ($flags) {
                return $flags->isNotEmpty();
            });
        })->filter(function ($months) {
            return $months->isNotEmpty();
        });

        dd($filterResults);
```

（結果例)
```php
Illuminate\Support\Collection {#650 ▼ // app/Http/Controllers/ProjectWorkController.php:463
  #items: array:22 [▶
    "EMP001" => Illuminate\Support\Collection {#647 ▶
      #items: array:3 [▶
        202306 => Illuminate\Support\Collection {#646 ▶
          #items: array:2 [▶
            0 => Illuminate\Support\Collection {#645 ▶
              #items: array:1 [▶
                0 => {#297 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ001"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "8.00"
                  +"ratio": 0.33
                }
              ]
              #escapeWhenCastingToString: false
            }
            1 => Illuminate\Support\Collection {#644 ▶
              #items: array:1 [▶
                0 => {#302 ▶
                  +"employee_id": "EMP001"
                  +"month": "202306"
                  +"project_code": "PRJ001"
                  +"budget_actual_flag": 1
                  +"total_work_hours": "9.00"
                  +"ratio": 0.41
                }
              ]
              #escapeWhenCastingToString: false
            }
          ]
          #escapeWhenCastingToString: false
        }
        202307 => Illuminate\Support\Collection {#643 ▶
          #items: array:2 [▶
            0 => Illuminate\Support\Collection {#642 ▶
              #items: array:1 [▶
                0 => {#306 ▶
                  +"employee_id": "EMP001"
                  +"month": "202307"
                  +"project_code": "PRJ001"
                  +"budget_actual_flag": 0
                  +"total_work_hours": "10.00"
                  +"ratio": 0.53
                }
              ]
              #escapeWhenCastingToString: false
            }
            1 => Illuminate\Support\Collection {#641 ▶
              #items: array:1 [▶
                0 => {#308 ▶
                  +"employee_id": "EMP001"
                  +"month": "202307"
                  +"project_code": "PRJ001"
                  +"budget_actual_flag": 1
                  +"total_work_hours": "4.00"
                  +"ratio": 0.2
                }
              ]
              #escapeWhenCastingToString: false
            }
          ]
          #escapeWhenCastingToString: false
        }

```