4 - Update BucketController with bucket type validation and constraints
This commit is contained in:
parent
da036ce97f
commit
e5dc7b0e21
1 changed files with 71 additions and 29 deletions
|
|
@ -3,6 +3,8 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Actions\CreateBucketAction;
|
use App\Actions\CreateBucketAction;
|
||||||
|
use App\Enums\BucketAllocationTypeEnum;
|
||||||
|
use App\Enums\BucketTypeEnum;
|
||||||
use App\Models\Bucket;
|
use App\Models\Bucket;
|
||||||
use App\Models\Scenario;
|
use App\Models\Scenario;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
@ -17,21 +19,7 @@ public function index(Scenario $scenario): JsonResponse
|
||||||
$buckets = $scenario->buckets()
|
$buckets = $scenario->buckets()
|
||||||
->orderedBySortOrder()
|
->orderedBySortOrder()
|
||||||
->get()
|
->get()
|
||||||
->map(function ($bucket) {
|
->map(fn ($bucket) => $this->formatBucketResponse($bucket));
|
||||||
return [
|
|
||||||
'id' => $bucket->uuid,
|
|
||||||
'name' => $bucket->name,
|
|
||||||
'priority' => $bucket->priority,
|
|
||||||
'sort_order' => $bucket->sort_order,
|
|
||||||
'allocation_type' => $bucket->allocation_type,
|
|
||||||
'allocation_value' => $bucket->allocation_value,
|
|
||||||
'allocation_type_label' => $bucket->getAllocationTypeLabel(),
|
|
||||||
'formatted_allocation_value' => $bucket->getFormattedAllocationValue(),
|
|
||||||
'current_balance' => $bucket->getCurrentBalance(),
|
|
||||||
'has_available_space' => $bucket->hasAvailableSpace(),
|
|
||||||
'available_space' => $bucket->getAvailableSpace(),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'buckets' => $buckets,
|
'buckets' => $buckets,
|
||||||
|
|
@ -42,22 +30,28 @@ public function store(Request $request, Scenario $scenario): JsonResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'allocation_type' => 'required|in:'.implode(',', [
|
'type' => 'required|in:'.implode(',', BucketTypeEnum::values()),
|
||||||
Bucket::TYPE_FIXED_LIMIT,
|
'allocation_type' => 'required|in:'.implode(',', BucketAllocationTypeEnum::values()),
|
||||||
Bucket::TYPE_PERCENTAGE,
|
|
||||||
Bucket::TYPE_UNLIMITED,
|
|
||||||
]),
|
|
||||||
'allocation_value' => 'nullable|numeric',
|
'allocation_value' => 'nullable|numeric',
|
||||||
'priority' => 'nullable|integer|min:1',
|
'priority' => 'nullable|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$type = BucketTypeEnum::from($validated['type']);
|
||||||
|
$allocationType = BucketAllocationTypeEnum::from($validated['allocation_type']);
|
||||||
|
|
||||||
|
$constraintError = $this->validateBucketTypeConstraints($type, $allocationType, $scenario);
|
||||||
|
if ($constraintError) {
|
||||||
|
return $constraintError;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$createBucketAction = new CreateBucketAction;
|
$createBucketAction = new CreateBucketAction;
|
||||||
$bucket = $createBucketAction->execute(
|
$bucket = $createBucketAction->execute(
|
||||||
$scenario,
|
$scenario,
|
||||||
$validated['name'],
|
$validated['name'],
|
||||||
$validated['allocation_type'],
|
$allocationType,
|
||||||
$validated['allocation_value'],
|
$type,
|
||||||
|
$validated['allocation_value'] ?? null,
|
||||||
$validated['priority'] ?? null
|
$validated['priority'] ?? null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -77,23 +71,28 @@ public function update(Request $request, Bucket $bucket): JsonResponse
|
||||||
{
|
{
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'allocation_type' => 'required|in:'.implode(',', [
|
'type' => 'required|in:'.implode(',', BucketTypeEnum::values()),
|
||||||
Bucket::TYPE_FIXED_LIMIT,
|
'allocation_type' => 'required|in:'.implode(',', BucketAllocationTypeEnum::values()),
|
||||||
Bucket::TYPE_PERCENTAGE,
|
|
||||||
Bucket::TYPE_UNLIMITED,
|
|
||||||
]),
|
|
||||||
'allocation_value' => 'nullable|numeric',
|
'allocation_value' => 'nullable|numeric',
|
||||||
'priority' => 'nullable|integer|min:1',
|
'priority' => 'nullable|integer|min:1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$type = BucketTypeEnum::from($validated['type']);
|
||||||
|
$allocationType = BucketAllocationTypeEnum::from($validated['allocation_type']);
|
||||||
|
|
||||||
|
$constraintError = $this->validateBucketTypeConstraints($type, $allocationType, $bucket->scenario, $bucket);
|
||||||
|
if ($constraintError) {
|
||||||
|
return $constraintError;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate allocation_value based on allocation_type
|
// Validate allocation_value based on allocation_type
|
||||||
$allocationValueRules = Bucket::allocationValueRules($validated['allocation_type']);
|
$allocationValueRules = Bucket::allocationValueRules($allocationType);
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'allocation_value' => $allocationValueRules,
|
'allocation_value' => $allocationValueRules,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Set allocation_value to null for unlimited buckets
|
// Set allocation_value to null for unlimited buckets
|
||||||
if ($validated['allocation_type'] === Bucket::TYPE_UNLIMITED) {
|
if ($allocationType === BucketAllocationTypeEnum::UNLIMITED) {
|
||||||
$validated['allocation_value'] = null;
|
$validated['allocation_value'] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +154,47 @@ public function updatePriorities(Request $request, Scenario $scenario): JsonResp
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate bucket type constraints (type+allocation compatibility, one overflow per scenario).
|
||||||
|
*/
|
||||||
|
private function validateBucketTypeConstraints(
|
||||||
|
BucketTypeEnum $type,
|
||||||
|
BucketAllocationTypeEnum $allocationType,
|
||||||
|
Scenario $scenario,
|
||||||
|
?Bucket $excludeBucket = null
|
||||||
|
): ?JsonResponse {
|
||||||
|
if ($type === BucketTypeEnum::OVERFLOW && $allocationType !== BucketAllocationTypeEnum::UNLIMITED) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Validation failed.',
|
||||||
|
'errors' => ['allocation_type' => ['Overflow buckets must use unlimited allocation type.']],
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type !== BucketTypeEnum::OVERFLOW && $allocationType === BucketAllocationTypeEnum::UNLIMITED) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Validation failed.',
|
||||||
|
'errors' => ['allocation_type' => ['Only overflow buckets can use unlimited allocation type.']],
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type === BucketTypeEnum::OVERFLOW) {
|
||||||
|
$query = $scenario->buckets()->where('type', BucketTypeEnum::OVERFLOW->value);
|
||||||
|
|
||||||
|
if ($excludeBucket) {
|
||||||
|
$query->where('id', '!=', $excludeBucket->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query->exists()) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Validation failed.',
|
||||||
|
'errors' => ['type' => ['A scenario can only have one overflow bucket.']],
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format bucket data for JSON response.
|
* Format bucket data for JSON response.
|
||||||
*/
|
*/
|
||||||
|
|
@ -163,6 +203,8 @@ private function formatBucketResponse(Bucket $bucket): array
|
||||||
return [
|
return [
|
||||||
'id' => $bucket->uuid,
|
'id' => $bucket->uuid,
|
||||||
'name' => $bucket->name,
|
'name' => $bucket->name,
|
||||||
|
'type' => $bucket->type,
|
||||||
|
'type_label' => $bucket->type->getLabel(),
|
||||||
'priority' => $bucket->priority,
|
'priority' => $bucket->priority,
|
||||||
'sort_order' => $bucket->sort_order,
|
'sort_order' => $bucket->sort_order,
|
||||||
'allocation_type' => $bucket->allocation_type,
|
'allocation_type' => $bucket->allocation_type,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue