210 lines
6.7 KiB
PHP
210 lines
6.7 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Http\Controllers\Api;
|
||
|
|
|
||
|
|
use App\Http\Controllers\Controller;
|
||
|
|
use App\Http\Resources\ExerciseResource;
|
||
|
|
use App\Http\Resources\WorkoutExerciseResource;
|
||
|
|
use App\Http\Resources\WorkoutResource;
|
||
|
|
use App\Models\Exercise;
|
||
|
|
use App\Models\Workout;
|
||
|
|
use App\Models\WorkoutExercise;
|
||
|
|
use Illuminate\Http\JsonResponse;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
|
||
|
|
class WorkoutController extends Controller
|
||
|
|
{
|
||
|
|
public function index(): JsonResponse
|
||
|
|
{
|
||
|
|
$workouts = Auth::user()->workouts()->with('assignedByTrainer')->latest()->paginate(10);
|
||
|
|
|
||
|
|
return $this->paginated($workouts);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function community(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$q = trim((string) $request->query('q', ''));
|
||
|
|
|
||
|
|
$workouts = Workout::query()
|
||
|
|
->where('is_public', true)
|
||
|
|
->with(['user:id,name', 'items'])
|
||
|
|
->when($q !== '', fn ($qb) => $qb->where('title', 'like', "%{$q}%"))
|
||
|
|
->latest()
|
||
|
|
->paginate(12)
|
||
|
|
->withQueryString();
|
||
|
|
|
||
|
|
return $this->paginated($workouts);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function store(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$data = $request->validate([
|
||
|
|
'title' => 'required|string|max:255',
|
||
|
|
'notes' => 'nullable|string',
|
||
|
|
'estimated_minutes' => 'nullable|integer|min:1|max:600',
|
||
|
|
]);
|
||
|
|
$workout = Auth::user()->workouts()->create($data);
|
||
|
|
|
||
|
|
return response()->json(['workout' => new WorkoutResource($workout)], 201);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function show(Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorizeView($workout);
|
||
|
|
$workout->load(['items.exercise', 'assignedByTrainer', 'user:id,name']);
|
||
|
|
|
||
|
|
return response()->json(['workout' => new WorkoutResource($workout)]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function searchExercises(Request $request, Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorizeWorkout($workout);
|
||
|
|
$q = trim((string) $request->query('q', ''));
|
||
|
|
|
||
|
|
$exercises = Exercise::query()
|
||
|
|
->with(['aliases' => fn ($rel) => $rel->where('user_id', Auth::id())])
|
||
|
|
->when($q !== '', fn ($qb) => $qb->where('name', 'like', "%{$q}%"))
|
||
|
|
->orderBy('name')
|
||
|
|
->limit(20)
|
||
|
|
->get(['id', 'name']);
|
||
|
|
|
||
|
|
return response()->json(ExerciseResource::collection($exercises));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function togglePublic(Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
if ($workout->user_id !== Auth::id()) {
|
||
|
|
abort(403);
|
||
|
|
}
|
||
|
|
$workout->is_public = !$workout->is_public;
|
||
|
|
$workout->save();
|
||
|
|
|
||
|
|
return response()->json(['workout' => new WorkoutResource($workout)]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function copy(Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
if (!$workout->is_public && $workout->user_id !== Auth::id()) {
|
||
|
|
abort(403);
|
||
|
|
}
|
||
|
|
|
||
|
|
$workout->loadMissing('items');
|
||
|
|
|
||
|
|
$copy = Auth::user()->workouts()->create([
|
||
|
|
'title' => $workout->title.' (copy)',
|
||
|
|
'notes' => $workout->notes,
|
||
|
|
'estimated_minutes' => $workout->estimated_minutes,
|
||
|
|
]);
|
||
|
|
|
||
|
|
foreach ($workout->items as $item) {
|
||
|
|
$copy->items()->create([
|
||
|
|
'exercise_id' => $item->exercise_id,
|
||
|
|
'position' => $item->position,
|
||
|
|
'sets' => $item->sets,
|
||
|
|
'reps' => $item->reps,
|
||
|
|
'weight' => $item->weight,
|
||
|
|
'rest_seconds' => $item->rest_seconds,
|
||
|
|
'notes' => $item->notes,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$copy->load(['items.exercise']);
|
||
|
|
|
||
|
|
return response()->json(['workout' => new WorkoutResource($copy)], 201);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function addExercise(Request $request, Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorizeWorkout($workout);
|
||
|
|
$data = $request->validate([
|
||
|
|
'exercise_id' => 'required|exists:exercises,id',
|
||
|
|
'position' => 'nullable|integer|min:1',
|
||
|
|
'sets' => 'nullable|integer|min:1|max:20',
|
||
|
|
'reps' => 'nullable|integer|min:1|max:200',
|
||
|
|
'weight' => 'nullable|numeric|min:0|max:999.99',
|
||
|
|
'rest_seconds' => 'nullable|integer|min:0|max:600',
|
||
|
|
'notes' => 'nullable|string',
|
||
|
|
]);
|
||
|
|
$nextPos = ($workout->items()->max('position') ?? 0) + 1;
|
||
|
|
$data['position'] = $data['position'] ?? $nextPos;
|
||
|
|
$item = $workout->items()->create($data);
|
||
|
|
$item->load('exercise');
|
||
|
|
|
||
|
|
return response()->json(['item' => new WorkoutExerciseResource($item)], 201);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function updateExercise(Request $request, Workout $workout, WorkoutExercise $item): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorizeWorkout($workout);
|
||
|
|
if ($item->workout_id !== $workout->id) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
$data = $request->validate([
|
||
|
|
'position' => 'nullable|integer|min:1',
|
||
|
|
'sets' => 'nullable|integer|min:1|max:20',
|
||
|
|
'reps' => 'nullable|integer|min:1|max:200',
|
||
|
|
'weight' => 'nullable|numeric|min:0|max:999.99',
|
||
|
|
'rest_seconds' => 'nullable|integer|min:0|max:600',
|
||
|
|
'notes' => 'nullable|string',
|
||
|
|
]);
|
||
|
|
$item->update($data);
|
||
|
|
$item->load('exercise');
|
||
|
|
|
||
|
|
return response()->json(['item' => new WorkoutExerciseResource($item)]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function removeExercise(Workout $workout, WorkoutExercise $item): JsonResponse
|
||
|
|
{
|
||
|
|
$this->authorizeWorkout($workout);
|
||
|
|
if ($item->workout_id !== $workout->id) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
$item->delete();
|
||
|
|
|
||
|
|
return response()->json(['message' => 'Exercise removed']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function destroy(Workout $workout): JsonResponse
|
||
|
|
{
|
||
|
|
if ($workout->user_id !== Auth::id()) {
|
||
|
|
abort(403);
|
||
|
|
}
|
||
|
|
$workout->delete();
|
||
|
|
|
||
|
|
return response()->json(['message' => 'Workout deleted']);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function authorizeWorkout(Workout $workout): void
|
||
|
|
{
|
||
|
|
if ($workout->user_id === Auth::id()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if ($workout->assigned_by_trainer_id && $workout->assigned_by_trainer_id === Auth::id()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
abort(403);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function authorizeView(Workout $workout): void
|
||
|
|
{
|
||
|
|
if ($workout->is_public) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$this->authorizeWorkout($workout);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function paginated($paginator): JsonResponse
|
||
|
|
{
|
||
|
|
return response()->json([
|
||
|
|
'data' => WorkoutResource::collection($paginator->items()),
|
||
|
|
'meta' => [
|
||
|
|
'current_page' => $paginator->currentPage(),
|
||
|
|
'last_page' => $paginator->lastPage(),
|
||
|
|
'per_page' => $paginator->perPage(),
|
||
|
|
'total' => $paginator->total(),
|
||
|
|
],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|