88 lines
3.1 KiB
PHP
88 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Actions;
|
|
|
|
use App\Enums\NutritionPlanSource;
|
|
use App\Enums\PacePreference;
|
|
use App\Enums\PhysicalActivityLevel;
|
|
use App\Enums\UserSex;
|
|
use App\Enums\WeightGoal;
|
|
use App\Models\User;
|
|
use App\Services\NutritionPlanCalculator;
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class RecalculateNutritionPlan
|
|
{
|
|
public function __construct(private NutritionPlanCalculator $calculator) {}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function execute(
|
|
User $user,
|
|
array $data,
|
|
NutritionPlanSource $source,
|
|
bool $completeOnboarding = false,
|
|
): User {
|
|
return DB::transaction(function () use ($user, $data, $source, $completeOnboarding): User {
|
|
$lockedUser = User::query()->lockForUpdate()->findOrFail($user->getKey());
|
|
$now = now();
|
|
|
|
if ($completeOnboarding && $lockedUser->hasCompletedNutritionOnboarding()) {
|
|
return $lockedUser->load(['activeNutritionPlan', 'latestWeightEntry']);
|
|
}
|
|
|
|
$lockedUser->update([
|
|
'height' => $data['heightCm'],
|
|
'target_weight' => $data['targetWeightKg'] ?? null,
|
|
'date_of_birth' => $data['dateOfBirth'],
|
|
'sex' => $data['sex'],
|
|
'physical_activity_level' => $data['physicalActivityLevel'],
|
|
'weight_goal' => $data['weightGoal'],
|
|
'pace_preference' => $data['pacePreference'],
|
|
...($completeOnboarding ? [
|
|
'onboarding_completed_at' => $now,
|
|
'nutrition_estimate_accepted_at' => $now,
|
|
] : []),
|
|
]);
|
|
|
|
$weightKg = (float) $data['weightKg'];
|
|
$latestWeight = $lockedUser->weightEntries()->latest('measured_at')->first();
|
|
|
|
if ($latestWeight === null || abs($latestWeight->weight_kg - $weightKg) >= 0.01) {
|
|
$lockedUser->weightEntries()->create([
|
|
'weight_kg' => $weightKg,
|
|
'measured_at' => $now,
|
|
]);
|
|
}
|
|
|
|
$lockedUser->nutritionPlans()
|
|
->where('is_active', true)
|
|
->update([
|
|
'is_active' => false,
|
|
'effective_until' => $now,
|
|
]);
|
|
|
|
$calculation = $this->calculator->calculate(
|
|
weightKg: $weightKg,
|
|
heightCm: (int) $data['heightCm'],
|
|
dateOfBirth: CarbonImmutable::parse($data['dateOfBirth']),
|
|
sex: UserSex::from($data['sex']),
|
|
activityLevel: PhysicalActivityLevel::from($data['physicalActivityLevel']),
|
|
weightGoal: WeightGoal::from($data['weightGoal']),
|
|
pacePreference: PacePreference::from($data['pacePreference']),
|
|
);
|
|
|
|
$lockedUser->nutritionPlans()->create([
|
|
...$calculation,
|
|
'source' => $source,
|
|
'is_active' => true,
|
|
'effective_from' => $now,
|
|
]);
|
|
|
|
return $lockedUser->fresh()->load(['activeNutritionPlan', 'latestWeightEntry']);
|
|
}, attempts: 3);
|
|
}
|
|
}
|