diff --git a/app/Http/Controllers/MealPostController.php b/app/Http/Controllers/MealPostController.php index 8db51ef..14df9c0 100644 --- a/app/Http/Controllers/MealPostController.php +++ b/app/Http/Controllers/MealPostController.php @@ -23,12 +23,20 @@ class MealPostController extends Controller { $perPage = max(1, min($request->integer('per_page', 15), 100)); - $mealPosts = MealPosts::query() - ->with('user:id,name,avatar_url') - ->withCount('likedByUsers') - ->withExists([ - 'likedByUsers as liked_by_current_user' => fn (Builder $query): Builder => $query->whereKey($request->user()->getKey()), - ]) + $mealPosts = $this->mealPostListQuery($request) + ->latest('eaten_at') + ->paginate($perPage) + ->withQueryString(); + + return MealPostsResource::collection($mealPosts); + } + + public function mine(Request $request): AnonymousResourceCollection + { + $perPage = max(1, min($request->integer('per_page', 15), 100)); + + $mealPosts = $this->mealPostListQuery($request) + ->where('user_id', $request->user()->getKey()) ->latest('eaten_at') ->paginate($perPage) ->withQueryString(); @@ -200,6 +208,16 @@ class MealPostController extends Controller ]); } + private function mealPostListQuery(Request $request): Builder + { + return MealPosts::query() + ->with('user:id,name,avatar_url') + ->withCount('likedByUsers') + ->withExists([ + 'likedByUsers as liked_by_current_user' => fn (Builder $query): Builder => $query->whereKey($request->user()->getKey()), + ]); + } + private function getValidatedDataWithImageUrl(MealPostsRequest $request): array { $data = $request->validated(); diff --git a/app/Http/Controllers/WorkoutSessionController.php b/app/Http/Controllers/WorkoutSessionController.php index 3e8b272..3b52d11 100644 --- a/app/Http/Controllers/WorkoutSessionController.php +++ b/app/Http/Controllers/WorkoutSessionController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; use App\Http\Requests\WorkoutSessionsRequest; use App\Http\Resources\WorkoutSessionsResource; +use Carbon\CarbonImmutable; use Illuminate\Http\JsonResponse; use Illuminate\Http\Resources\Json\AnonymousResourceCollection; @@ -11,8 +12,19 @@ class WorkoutSessionController extends Controller { public function index(WorkoutSessionsRequest $request): AnonymousResourceCollection { - $workoutSessions = $request->user() - ->workouts() + $validated = $request->validated(); + $workoutSessionsQuery = $request->user()->workouts(); + + if (isset($validated['date'])) { + $referenceDate = CarbonImmutable::createFromFormat('!Y-m-d', $validated['date']); + + $workoutSessionsQuery->whereBetween('started_at', [ + $referenceDate->startOfDay(), + $referenceDate->endOfDay(), + ]); + } + + $workoutSessions = $workoutSessionsQuery ->latest('started_at') ->paginate($request->integer('per_page', 15)) ->withQueryString(); diff --git a/app/Http/Requests/WorkoutSessionsRequest.php b/app/Http/Requests/WorkoutSessionsRequest.php index 8e6abfb..bcb6ee6 100644 --- a/app/Http/Requests/WorkoutSessionsRequest.php +++ b/app/Http/Requests/WorkoutSessionsRequest.php @@ -21,6 +21,7 @@ class WorkoutSessionsRequest extends FormRequest { if ($this->isMethod('get')) { return [ + 'date' => ['sometimes', 'date_format:Y-m-d'], 'per_page' => ['sometimes', 'integer', 'min:1', 'max:100'], ]; } diff --git a/routes/api.php b/routes/api.php index da13e50..3d58163 100644 --- a/routes/api.php +++ b/routes/api.php @@ -51,6 +51,7 @@ Route::middleware(['auth:sanctum', 'verified'])->group(function (): void { // Meals Route::middleware(['auth:sanctum', 'verified'])->group(function (): void { + Route::get('me/meal-posts', [MealPostController::class, 'mine'])->name('meal-posts.mine'); Route::get('meal-posts/stats', [MealPostController::class, 'stats']); Route::get('meal-posts/calendar', [MealPostController::class, 'calendar']); Route::post('meal-posts/analyze-image', [MealImageAnalysisController::class, 'store']); diff --git a/tests/Feature/MealPostListTest.php b/tests/Feature/MealPostListTest.php new file mode 100644 index 0000000..a7ef202 --- /dev/null +++ b/tests/Feature/MealPostListTest.php @@ -0,0 +1,37 @@ +create(); + $otherUser = User::factory()->create(); + + $oldMealPost = MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-05-18 12:00:00', + ]); + $latestMealPost = MealPosts::factory()->for($user, 'user')->create([ + 'eaten_at' => '2026-05-19 12:00:00', + ]); + $otherMealPost = MealPosts::factory()->for($otherUser, 'user')->create([ + 'eaten_at' => '2026-05-20 12:00:00', + ]); + + Sanctum::actingAs($user); + + $response = $this->getJson('/api/me/meal-posts'); + + $response + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.id', $latestMealPost->id) + ->assertJsonPath('data.1.id', $oldMealPost->id) + ->assertJsonMissing([ + 'id' => $otherMealPost->id, + ]); +}); + diff --git a/tests/Feature/WorkoutSessionsTest.php b/tests/Feature/WorkoutSessionsTest.php index c50fec1..dac5fa9 100644 --- a/tests/Feature/WorkoutSessionsTest.php +++ b/tests/Feature/WorkoutSessionsTest.php @@ -97,6 +97,36 @@ it('returns workouts for the authenticated user', function () { ]); }); +it('filters workouts by date', function () { + $user = User::factory()->create(); + + $todayWorkoutSession = WorkoutSessions::factory()->for($user, 'user')->create([ + 'calories_burned' => 450, + 'started_at' => '2026-05-19 07:30:00', + ]); + $eveningWorkoutSession = WorkoutSessions::factory()->for($user, 'user')->create([ + 'calories_burned' => 120, + 'started_at' => '2026-05-19 19:30:00', + ]); + $yesterdayWorkoutSession = WorkoutSessions::factory()->for($user, 'user')->create([ + 'calories_burned' => 320, + 'started_at' => '2026-05-18 10:00:00', + ]); + + Sanctum::actingAs($user); + + $response = $this->getJson('/api/workouts?date=2026-05-19'); + + $response + ->assertOk() + ->assertJsonCount(2, 'data') + ->assertJsonPath('data.0.id', $eveningWorkoutSession->id) + ->assertJsonPath('data.1.id', $todayWorkoutSession->id) + ->assertJsonMissing([ + 'id' => $yesterdayWorkoutSession->id, + ]); +}); + it('validates the workouts pagination size', function () { Sanctum::actingAs(User::factory()->create()); @@ -106,6 +136,15 @@ it('validates the workouts pagination size', function () { ->assertJsonValidationErrors(['per_page']); }); +it('validates the workouts date filter', function () { + Sanctum::actingAs(User::factory()->create()); + + $this + ->getJson('/api/workouts?date=2026-13-01') + ->assertUnprocessable() + ->assertJsonValidationErrors(['date']); +}); + it('validates workout creation data', function () { Sanctum::actingAs(User::factory()->create());