perf: spots/overview — 100+ requêtes individuelles → 1 requête cachée 30 min

Avant : SargassesMap faisait 1 appel /api/spots (API Platform) puis
N appels /api/spots/{id}/score (un par spot, ~100+ pour la Guadeloupe).

Après : un seul GET /api/spots/overview (LATERAL JOIN spots+scores)
avec Cache-Control: public, max-age=1800, stale-while-revalidate=300.

Les marqueurs colorés s'affichent immédiatement depuis la réponse unique ;
/api/spots/{id}/score n'est appelé qu'au clic sur un spot (SpotPanel).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gwadaking
2026-04-14 12:43:38 -04:00
parent 14d2acc39b
commit 83690b94ab
3 changed files with 63 additions and 22 deletions

View File

@@ -20,6 +20,46 @@ class SpotScoreController extends AbstractController
private Connection $connection,
) {}
/**
* GET /api/spots/overview
*
* Retourne tous les spots avec leur dernier score en UNE seule requête.
* Remplace les N appels individuels à /api/spots/{id}/score pour le rendu
* initial de la carte.
*
* Cache HTTP 30 min (scores mis à jour au max 1-2×/jour par ingestion).
*/
#[Route('/overview', name: 'overview', methods: ['GET'])]
public function overview(): JsonResponse
{
$rows = $this->connection->fetchAllAssociative(
"SELECT
cp.id::text,
cp.name,
cp.type,
cp.region,
ST_X(cp.geometry::geometry) AS longitude,
ST_Y(cp.geometry::geometry) AS latitude,
ist.score,
ist.level,
ROUND(CAST(ist.distance_to_nearest_sargassum AS numeric), 2) AS distance_km
FROM coastal_point cp
LEFT JOIN LATERAL (
SELECT score, level, distance_to_nearest_sargassum
FROM impact_score
WHERE coastal_point_id = cp.id
ORDER BY timestamp DESC
LIMIT 1
) ist ON true
ORDER BY cp.name"
);
$response = $this->json($rows);
$response->headers->set('Cache-Control', 'public, max-age=1800, stale-while-revalidate=300');
return $response;
}
/**
* GET /api/spots/{id}/score
*