From 69261a1a2d859eebfbfa0602dc0b237c54f490e7 Mon Sep 17 00:00:00 2001 From: Mateo Date: Sun, 22 Mar 2026 18:55:45 +0100 Subject: [PATCH] feat: refactor task and column management by using services and resources --- .github/workflows/deploy.yml | 6 +- app/Http/Controllers/ColumnController.php | 15 ++- app/Http/Controllers/TaskController.php | 50 ++++------ .../Controllers/TaskSequenceController.php | 41 +------- app/Http/Resources/ColumnResource.php | 25 +++++ app/Http/Resources/TaskResource.php | 32 +++++++ app/Providers/AppServiceProvider.php | 3 +- app/Services/ColumnService.php | 40 ++++++++ app/Services/TaskService.php | 96 +++++++++++++++++++ 9 files changed, 228 insertions(+), 80 deletions(-) create mode 100644 app/Http/Resources/ColumnResource.php create mode 100644 app/Http/Resources/TaskResource.php create mode 100644 app/Services/ColumnService.php create mode 100644 app/Services/TaskService.php diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 367f202..9527523 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,6 +3,7 @@ name: Deploy to Debian on: push: branches: [ main ] + workflow_dispatch: jobs: deploy: @@ -21,6 +22,9 @@ jobs: # Reconstruction des containers avec le nouveau Dockerfile (PHP 8.4 + Node) docker compose up -d --build + + # Eviter l'erreur dubious ownership de Git dans le conteneur + docker exec laravel-app git config --global --add safe.directory /var/www # 2. Installer les dépendances PHP docker exec laravel-app composer install --ignore-platform-reqs @@ -34,5 +38,5 @@ jobs: docker exec laravel-app php artisan migrate --force # 5. Nettoyage des droits et des images - docker exec laravel-app chown -R www-data:www-data storage bootstrap/cache database + docker exec laravel-app sh -lc "mkdir -p storage bootstrap/cache database && touch database/database.sqlite && chown -R 1000:33 storage bootstrap/cache && chown 1000:33 database/database.sqlite" docker image prune -f \ No newline at end of file diff --git a/app/Http/Controllers/ColumnController.php b/app/Http/Controllers/ColumnController.php index f9d2560..231bee4 100644 --- a/app/Http/Controllers/ColumnController.php +++ b/app/Http/Controllers/ColumnController.php @@ -3,24 +3,21 @@ namespace App\Http\Controllers; use App\Models\Column; +use App\Services\ColumnService; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; class ColumnController extends Controller { + public function __construct(private ColumnService $columnService) {} + public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], ]); - $order = Column::where('team_id', $request->user()->team_id)->max('order') + 1 ?? 0; - - Column::create([ - 'name' => $validated['name'], - 'team_id' => $request->user()->team_id, - 'order' => $order, - ]); + $this->columnService->createColumn($validated, $request->user()); return back(); } @@ -35,7 +32,7 @@ public function update(Request $request, Column $column): RedirectResponse 'name' => ['required', 'string', 'max:255'], ]); - $column->update($validated); + $this->columnService->updateColumn($column, $validated); return back(); } @@ -46,7 +43,7 @@ public function destroy(Request $request, Column $column): RedirectResponse abort(403); } - $column->delete(); + $this->columnService->deleteColumn($column); return back(); } diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index c340470..abf2e22 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -4,6 +4,8 @@ use App\Models\Task; use App\Models\Column; +use App\Services\TaskService; +use App\Http\Resources\ColumnResource; use Inertia\Inertia; use Inertia\Response; use Illuminate\Http\Request; @@ -11,43 +13,31 @@ use App\Http\Requests\TaskCreateRequest; use Illuminate\Http\RedirectResponse; use App\Http\Requests\TaskUpdateRequest; -use App\Jobs\IncrementTeamCompletedTasks; class TaskController extends Controller { + public function __construct(private TaskService $taskService) {} + public function index(Request $request): Response { + $columns = Column::query() + ->with([ + 'tasks' => function ($query) { + $query->with('creator:id,name')->orderBy('order'); + } + ]) + ->where('team_id', $request->user()->team_id) + ->orderBy('order') + ->get(); + return Inertia::render('Tasks', [ - 'columns' => Column::query() - ->with([ - 'tasks' => function ($query) { - $query->with('creator:id,name')->orderBy('order'); - } - ]) - ->where('team_id', $request->user()->team_id) - ->orderBy('order') - ->get(), + 'columns' => ColumnResource::collection($columns), ]); } public function store(TaskCreateRequest $request): RedirectResponse { - $columnId = $request->validated('column_id') ?? null; - - if (!$columnId) { - $columnId = Column::where('team_id', $request->user()->team_id)->orderBy('order')->value('id'); - } - - $order = Task::where('column_id', $columnId)->max('order'); - $order = $order !== null ? $order + 1 : 0; - - Task::query()->create([ - ...$request->safe()->except('column_id'), - 'team_id' => $request->user()->team_id, - 'created_by' => $request->user()->id, - 'column_id' => $columnId, - 'order' => $order, - ]); + $this->taskService->createTask($request->safe()->all(), $request->user()); return to_route('tasks.index'); } @@ -58,11 +48,7 @@ public function update(TaskUpdateRequest $request, Task $task): RedirectResponse abort(403, 'You are not authorized to update this task.'); } - $task->update($request->validated()); - - if ($task->completed) { - IncrementTeamCompletedTasks::dispatch($task->team_id); - } + $this->taskService->updateTask($task, $request->validated()); return back(); } @@ -73,7 +59,7 @@ public function destroy(Request $request, Task $task): RedirectResponse abort(403, 'You are not authorized to delete this task.'); } - $task->delete(); + $this->taskService->deleteTask($task); return back(); } diff --git a/app/Http/Controllers/TaskSequenceController.php b/app/Http/Controllers/TaskSequenceController.php index 428d409..a2ab4a7 100644 --- a/app/Http/Controllers/TaskSequenceController.php +++ b/app/Http/Controllers/TaskSequenceController.php @@ -3,12 +3,14 @@ namespace App\Http\Controllers; use App\Models\Task; +use App\Services\TaskService; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Facades\DB; class TaskSequenceController extends Controller { + public function __construct(private TaskService $taskService) {} + public function update(Request $request, Task $task): RedirectResponse { if ($task->team_id !== $request->user()->team_id) { @@ -20,42 +22,7 @@ public function update(Request $request, Task $task): RedirectResponse 'order' => ['required', 'integer', 'min:0'], ]); - $newColumnId = $validated['column_id']; - $newOrder = $validated['order']; - - $oldColumnId = $task->column_id; - $oldOrder = $task->order; - - DB::transaction(function () use ($task, $newColumnId, $newOrder, $oldColumnId, $oldOrder) { - if ($oldColumnId == $newColumnId) { - // Moving within the same column - if ($oldOrder < $newOrder) { - Task::where('column_id', $newColumnId) - ->whereBetween('order', [$oldOrder + 1, $newOrder]) - ->decrement('order'); - } elseif ($oldOrder > $newOrder) { - Task::where('column_id', $newColumnId) - ->whereBetween('order', [$newOrder, $oldOrder - 1]) - ->increment('order'); - } - } else { - // Moving to a different column - // Shift tasks in old column down - Task::where('column_id', $oldColumnId) - ->where('order', '>', $oldOrder) - ->decrement('order'); - - // Shift tasks in new column up to make room - Task::where('column_id', $newColumnId) - ->where('order', '>=', $newOrder) - ->increment('order'); - } - - $task->update([ - 'column_id' => $newColumnId, - 'order' => $newOrder, - ]); - }); + $this->taskService->updateSequence($task, $validated['column_id'], $validated['order']); return back(); } diff --git a/app/Http/Resources/ColumnResource.php b/app/Http/Resources/ColumnResource.php new file mode 100644 index 0000000..633a72d --- /dev/null +++ b/app/Http/Resources/ColumnResource.php @@ -0,0 +1,25 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'team_id' => $this->team_id, + 'name' => $this->name, + 'order' => $this->order, + 'tasks' => TaskResource::collection($this->whenLoaded('tasks')), + ]; + } +} diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php new file mode 100644 index 0000000..ca97331 --- /dev/null +++ b/app/Http/Resources/TaskResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'team_id' => $this->team_id, + 'column_id' => $this->column_id, + 'order' => $this->order, + 'title' => $this->title, + 'completed' => (bool)$this->completed, + 'due_date' => $this->due_date, + 'created_by' => $this->created_by, + 'creator' => $this->whenLoaded('creator', fn () => [ + 'id' => $this->creator->id, + 'name' => $this->creator->name, + ]), + ]; + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..82fd964 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Illuminate\Http\Resources\Json\JsonResource; class AppServiceProvider extends ServiceProvider { @@ -19,6 +20,6 @@ public function register(): void */ public function boot(): void { - // + JsonResource::withoutWrapping(); } } diff --git a/app/Services/ColumnService.php b/app/Services/ColumnService.php new file mode 100644 index 0000000..6c0ddd8 --- /dev/null +++ b/app/Services/ColumnService.php @@ -0,0 +1,40 @@ +team_id)->max('order'); + $order = $order !== null ? $order + 1 : 0; + + return Column::create([ + 'name' => $data['name'], + 'team_id' => $user->team_id, + 'order' => $order, + ]); + } + + /** + * Update an existing column. + */ + public function updateColumn(Column $column, array $data): bool + { + return $column->update($data); + } + + /** + * Delete a column. + */ + public function deleteColumn(Column $column): ?bool + { + return $column->delete(); + } +} diff --git a/app/Services/TaskService.php b/app/Services/TaskService.php new file mode 100644 index 0000000..ac35dd2 --- /dev/null +++ b/app/Services/TaskService.php @@ -0,0 +1,96 @@ +team_id)->orderBy('order')->value('id'); + } + + $order = Task::where('column_id', $columnId)->max('order'); + $order = $order !== null ? $order + 1 : 0; + + unset($data['column_id']); + + return Task::create(array_merge($data, [ + 'team_id' => $user->team_id, + 'created_by' => $user->id, + 'column_id' => $columnId, + 'order' => $order, + ])); + } + + /** + * Update an existing task. + */ + public function updateTask(Task $task, array $data): bool + { + $updated = $task->update($data); + + if ($updated && isset($data['completed']) && $data['completed']) { + IncrementTeamCompletedTasks::dispatch($task->team_id); + } + + return $updated; + } + + /** + * Update the sequence and column of a task inside the Kanban board. + */ + public function updateSequence(Task $task, int $newColumnId, int $newOrder): void + { + $oldColumnId = $task->column_id; + $oldOrder = $task->order; + + DB::transaction(function () use ($task, $newColumnId, $newOrder, $oldColumnId, $oldOrder) { + if ($oldColumnId == $newColumnId) { + // Moving within the same column + if ($oldOrder < $newOrder) { + Task::where('column_id', $newColumnId) + ->whereBetween('order', [$oldOrder + 1, $newOrder]) + ->decrement('order'); + } elseif ($oldOrder > $newOrder) { + Task::where('column_id', $newColumnId) + ->whereBetween('order', [$newOrder, $oldOrder - 1]) + ->increment('order'); + } + } else { + // Moving to a different column + Task::where('column_id', $oldColumnId) + ->where('order', '>', $oldOrder) + ->decrement('order'); + + Task::where('column_id', $newColumnId) + ->where('order', '>=', $newOrder) + ->increment('order'); + } + + $task->update([ + 'column_id' => $newColumnId, + 'order' => $newOrder, + ]); + }); + } + + /** + * Delete a task. + */ + public function deleteTask(Task $task): ?bool + { + return $task->delete(); + } +}