115 lines
5.6 KiB
PHP
115 lines
5.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/_bootstrap.php';
|
|
api_method('GET');
|
|
|
|
$planUuid = $_GET['id'] ?? null;
|
|
if ($planUuid !== null && (!is_string($planUuid) || !preg_match('/^[0-9a-f-]{36}$/i', $planUuid))) {
|
|
api_json(['error' => 'invalid_plan_id'], 400);
|
|
}
|
|
|
|
try {
|
|
$database = Database::getInstance();
|
|
$connection = $database->getConnection();
|
|
if ($planUuid !== null) {
|
|
$planQuery = $database->prepare(
|
|
'SELECT plan_uuid, slug, title_ar, summary_ar, goal, level, weeks_duration, source_notes, safety_notes_ar, content_revision FROM training_plans WHERE is_published = 1 AND plan_uuid = ? LIMIT 1'
|
|
);
|
|
$planQuery->bind_param('s', $planUuid);
|
|
} else {
|
|
$planQuery = $database->prepare(
|
|
'SELECT plan_uuid, slug, title_ar, summary_ar, goal, level, weeks_duration, source_notes, safety_notes_ar, content_revision FROM training_plans WHERE is_published = 1 ORDER BY title_ar'
|
|
);
|
|
}
|
|
$planQuery->execute();
|
|
$planResult = $planQuery->get_result();
|
|
$plans = [];
|
|
while ($plan = $planResult->fetch_assoc()) {
|
|
$plan['weeks_duration'] = (int) $plan['weeks_duration'];
|
|
$plan['content_revision'] = (int) $plan['content_revision'];
|
|
$plan['source_notes'] = json_decode($plan['source_notes'] ?? '[]', true) ?? [];
|
|
$plan['safety_notes_ar'] = json_decode($plan['safety_notes_ar'], true) ?? [];
|
|
$plans[$plan['plan_uuid']] = $plan;
|
|
}
|
|
$planQuery->close();
|
|
|
|
if ($planUuid !== null && !$plans) {
|
|
api_json(['error' => 'plan_not_found'], 404);
|
|
}
|
|
if (!$plans) {
|
|
header('Cache-Control: public, max-age=60, stale-while-revalidate=300');
|
|
api_json(['plans' => []]);
|
|
}
|
|
|
|
$planUuids = array_keys($plans);
|
|
$ids = array_fill(0, count($planUuids), '?');
|
|
$sessionQuery = $database->prepare(
|
|
'SELECT s.plan_id, p.plan_uuid, s.week_number, s.day_number, s.title_ar, s.session_type, s.duration_minutes, s.intensity, s.notes_ar, e.exercise_uuid, e.slug AS exercise_slug, e.title_ar AS exercise_title_ar, e.instructions_ar, e.target_muscles, e.equipment, e.gif_url, e.gif_poster_url, e.duration_seconds AS exercise_duration_seconds, e.repetitions, e.safety_notes_ar AS exercise_safety_notes_ar, alt.exercise_uuid AS alternative_exercise_uuid, se.sort_order AS exercise_sort_order, se.sets, se.reps, se.duration_seconds, se.rest_seconds FROM training_plan_sessions s JOIN training_plans p ON p.id = s.plan_id LEFT JOIN training_session_exercises se ON se.session_id = s.id LEFT JOIN exercises e ON e.id = se.exercise_id AND e.is_published = 1 LEFT JOIN exercises alt ON alt.id = e.alternative_exercise_id AND alt.is_published = 1 WHERE p.plan_uuid IN (' . implode(',', $ids) . ') AND p.is_published = 1 ORDER BY p.title_ar, s.week_number, s.day_number, se.sort_order'
|
|
);
|
|
$bindArguments = [str_repeat('s', count($planUuids))];
|
|
foreach ($planUuids as $index => $_planUuid) {
|
|
$bindArguments[] = &$planUuids[$index];
|
|
}
|
|
call_user_func_array([$sessionQuery, 'bind_param'], $bindArguments);
|
|
$sessionQuery->execute();
|
|
$sessionResult = $sessionQuery->get_result();
|
|
|
|
foreach ($plans as &$plan) {
|
|
$plan['sessions'] = [];
|
|
}
|
|
unset($plan);
|
|
|
|
while ($row = $sessionResult->fetch_assoc()) {
|
|
$uuid = $row['plan_uuid'];
|
|
$week = (int) $row['week_number'];
|
|
$day = (int) $row['day_number'];
|
|
$key = $week . ':' . $day;
|
|
if (!isset($plans[$uuid]['sessions'][$key])) {
|
|
$plans[$uuid]['sessions'][$key] = [
|
|
'week' => $week,
|
|
'day' => $day,
|
|
'title_ar' => $row['title_ar'],
|
|
'type' => $row['session_type'],
|
|
'duration_minutes' => (int) $row['duration_minutes'],
|
|
'intensity' => $row['intensity'],
|
|
'notes_ar' => $row['notes_ar'],
|
|
'exercises' => [],
|
|
];
|
|
}
|
|
if ($row['exercise_uuid'] !== null) {
|
|
$plans[$uuid]['sessions'][$key]['exercises'][] = [
|
|
'exercise_uuid' => $row['exercise_uuid'],
|
|
'slug' => $row['exercise_slug'],
|
|
'title_ar' => $row['exercise_title_ar'],
|
|
'instructions_ar' => json_decode($row['instructions_ar'], true) ?? [],
|
|
'target_muscles' => json_decode($row['target_muscles'], true) ?? [],
|
|
'equipment' => json_decode($row['equipment'], true) ?? [],
|
|
'gif_url' => $row['gif_url'],
|
|
'gif_poster_url' => $row['gif_poster_url'],
|
|
'duration_seconds' => $row['duration_seconds'] === null ? null : (int) $row['duration_seconds'],
|
|
'repetitions' => $row['repetitions'],
|
|
'safety_notes_ar' => json_decode($row['exercise_safety_notes_ar'] ?? '[]', true) ?? [],
|
|
'alternative_exercise_uuid' => $row['alternative_exercise_uuid'],
|
|
'prescription' => [
|
|
'sets' => $row['sets'] === null ? null : (int) $row['sets'],
|
|
'reps' => $row['reps'],
|
|
'duration_seconds' => $row['duration_seconds'] === null ? null : (int) $row['duration_seconds'],
|
|
'rest_seconds' => (int) $row['rest_seconds'],
|
|
],
|
|
];
|
|
}
|
|
}
|
|
$sessionQuery->close();
|
|
|
|
foreach ($plans as &$plan) {
|
|
$plan['sessions'] = array_values($plan['sessions']);
|
|
}
|
|
unset($plan);
|
|
header('Cache-Control: public, max-age=60, stale-while-revalidate=300');
|
|
api_json(['plans' => array_values($plans)]);
|
|
} catch (Throwable $exception) {
|
|
error_log('Training plans unavailable: ' . $exception->getMessage());
|
|
api_json(['error' => 'service_unavailable'], 503);
|
|
}
|