buckets/app/Http/Controllers/BucketController.php

218 lines
7.4 KiB
PHP
Raw Normal View History

2025-12-29 23:32:05 +01:00
<?php
namespace App\Http\Controllers;
use App\Actions\CreateBucketAction;
use App\Models\Bucket;
use App\Models\Scenario;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;
class BucketController extends Controller
{
public function index(Scenario $scenario): JsonResponse
{
$buckets = $scenario->buckets()
->orderedBySortOrder()
->get()
->map(function ($bucket) {
return [
'id' => $bucket->id,
'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([
'buckets' => $buckets
]);
}
public function store(Request $request, Scenario $scenario): JsonResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'allocation_type' => 'required|in:' . implode(',', [
Bucket::TYPE_FIXED_LIMIT,
Bucket::TYPE_PERCENTAGE,
Bucket::TYPE_UNLIMITED,
]),
'allocation_value' => 'nullable|numeric',
'priority' => 'nullable|integer|min:1',
]);
try {
$createBucketAction = new CreateBucketAction();
$bucket = $createBucketAction->execute(
$scenario,
$validated['name'],
$validated['allocation_type'],
$validated['allocation_value'],
$validated['priority'] ?? null
);
return response()->json([
'bucket' => $this->formatBucketResponse($bucket),
'message' => 'Bucket created successfully.'
], 201);
} catch (InvalidArgumentException $e) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['allocation_value' => [$e->getMessage()]]
], 422);
}
}
public function update(Request $request, Bucket $bucket): JsonResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'allocation_type' => 'required|in:' . implode(',', [
Bucket::TYPE_FIXED_LIMIT,
Bucket::TYPE_PERCENTAGE,
Bucket::TYPE_UNLIMITED,
]),
'allocation_value' => 'nullable|numeric',
'priority' => 'nullable|integer|min:1',
]);
// Validate allocation_value based on allocation_type
$allocationValueRules = Bucket::allocationValueRules($validated['allocation_type']);
$request->validate([
'allocation_value' => $allocationValueRules,
]);
// Set allocation_value to null for unlimited buckets
if ($validated['allocation_type'] === Bucket::TYPE_UNLIMITED) {
$validated['allocation_value'] = null;
}
// Handle priority change if needed
if (isset($validated['priority']) && $validated['priority'] !== $bucket->priority) {
$this->updateBucketPriority($bucket, $validated['priority']);
$validated['sort_order'] = $validated['priority'];
}
$bucket->update($validated);
return response()->json([
'bucket' => $this->formatBucketResponse($bucket),
'message' => 'Bucket updated successfully.'
]);
}
/**
* Remove the specified bucket.
*/
public function destroy(Bucket $bucket): JsonResponse
{
$scenarioId = $bucket->scenario_id;
$deletedPriority = $bucket->priority;
$bucket->delete();
// Shift remaining priorities down to fill the gap
$this->shiftPrioritiesDown($scenarioId, $deletedPriority);
return response()->json([
'message' => 'Bucket deleted successfully.'
]);
}
/**
* Update bucket priorities (for drag-and-drop reordering).
*/
public function updatePriorities(Request $request, Scenario $scenario): JsonResponse
{
$validated = $request->validate([
'bucket_priorities' => 'required|array',
'bucket_priorities.*.id' => 'required|exists:buckets,id',
'bucket_priorities.*.priority' => 'required|integer|min:1',
]);
foreach ($validated['bucket_priorities'] as $bucketData) {
$bucket = Bucket::find($bucketData['id']);
if ($bucket && $bucket->scenario_id === $scenario->id) {
$bucket->update([
'priority' => $bucketData['priority'],
'sort_order' => $bucketData['priority'],
]);
}
}
return response()->json([
'message' => 'Bucket priorities updated successfully.'
]);
}
/**
* Format bucket data for JSON response.
*/
private function formatBucketResponse(Bucket $bucket): array
{
return [
'id' => $bucket->id,
'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(),
];
}
/**
* Shift priorities down to fill gap after deletion.
*/
private function shiftPrioritiesDown(int $scenarioId, int $deletedPriority): void
{
Bucket::query()
->where('scenario_id', $scenarioId)
->where('priority', '>', $deletedPriority)
->decrement('priority');
}
/**
* Update a bucket's priority and adjust other buckets accordingly.
*/
private function updateBucketPriority(Bucket $bucket, int $newPriority): void
{
$oldPriority = $bucket->priority;
$scenario = $bucket->scenario;
if ($newPriority === $oldPriority) {
return;
}
if ($newPriority < $oldPriority) {
// Moving up - shift others down
$scenario->buckets()
->where('id', '!=', $bucket->id)
->whereBetween('priority', [$newPriority, $oldPriority - 1])
->increment('priority');
} else {
// Moving down - shift others up
$scenario->buckets()
->where('id', '!=', $bucket->id)
->whereBetween('priority', [$oldPriority + 1, $newPriority])
->decrement('priority');
}
$bucket->update(['priority' => $newPriority]);
}
}