前回で実績のみ対応した。(誤差を求め、特定の値(filter)を取得する)
今回は両方(実績、予算)対応する。
その場合であるが、グループ化を3つの変数に対し行うことになりネストが深くなる。
今回はコレクション型でforeachでも扱えるのでそれを使用する。
モデル
LaravelでDBファザードのDB::selectで対応と同じ。
コントローラ
<?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
等3つの記載は冗長になる。)
(結果例)
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()で返している。
$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);
(結果例)
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
}