Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Functional\Exercises\Http\Controllers;
|
||||
|
||||
use Functional\Exercises\Http\Resources\ExerciseListResource;
|
||||
use Functional\Exercises\Http\Resources\ExerciseResource;
|
||||
use Functional\Exercises\Models\Exercise;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
|
||||
/**
|
||||
* Catalogue d'exercices : lecture seule et public, aucun scoping par utilisateur.
|
||||
*/
|
||||
class ExercisesController
|
||||
{
|
||||
private const DEFAULT_LOCALE = 'fr';
|
||||
|
||||
private const DEFAULT_PER_PAGE = 20;
|
||||
|
||||
/** GET /api/exercises — liste filtrable (search/body_part/equipment/target/category) et paginée. */
|
||||
public function index(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$exercises = Exercise::query()
|
||||
->search($request->query('search'))
|
||||
->when($request->query('body_part'), fn ($query, $bodyPart) => $query->where('body_part', $bodyPart))
|
||||
->when($request->query('equipment'), fn ($query, $equipment) => $query->where('equipment', $equipment))
|
||||
->when($request->query('target'), fn ($query, $target) => $query->where('target', $target))
|
||||
->when($request->query('category'), fn ($query, $category) => $query->where('category', $category))
|
||||
->orderBy('name')
|
||||
->paginate((int) $request->query('per_page', self::DEFAULT_PER_PAGE));
|
||||
|
||||
return ExerciseListResource::collection($exercises);
|
||||
}
|
||||
|
||||
/** GET /api/exercises/{exercise} — détail avec instructions dans la locale demandée. */
|
||||
public function show(Request $request, Exercise $exercise): ExerciseResource
|
||||
{
|
||||
return new ExerciseResource($exercise, $this->resolveLocale($request));
|
||||
}
|
||||
|
||||
/** GET /api/exercises/facets — valeurs distinctes pour alimenter les filtres du front. */
|
||||
public function facets(): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'body_parts' => Exercise::query()->whereNotNull('body_part')->distinct()->orderBy('body_part')->pluck('body_part')->values(),
|
||||
'equipments' => Exercise::query()->whereNotNull('equipment')->distinct()->orderBy('equipment')->pluck('equipment')->values(),
|
||||
'targets' => Exercise::query()->whereNotNull('target')->distinct()->orderBy('target')->pluck('target')->values(),
|
||||
'categories' => Exercise::query()->whereNotNull('category')->distinct()->orderBy('category')->pluck('category')->values(),
|
||||
]);
|
||||
}
|
||||
|
||||
/** Résout la locale demandée : paramètre `locale`, sinon header Accept-Language, sinon 'fr'. */
|
||||
private function resolveLocale(Request $request): string
|
||||
{
|
||||
$locale = $request->query('locale');
|
||||
|
||||
if ($locale) {
|
||||
return (string) $locale;
|
||||
}
|
||||
|
||||
$acceptLanguage = $request->header('Accept-Language');
|
||||
|
||||
if (! $acceptLanguage) {
|
||||
return self::DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
// Le header peut lister plusieurs langues pondérées ("fr-FR,fr;q=0.9,en;q=0.8") : on ne garde que la première.
|
||||
$firstLanguage = strtok($acceptLanguage, ',');
|
||||
$languageTag = strtok($firstLanguage, ';');
|
||||
|
||||
return $languageTag !== false ? $languageTag : self::DEFAULT_LOCALE;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user