Files
survey/api/surveys.php
T

534 lines
23 KiB
PHP

<?php
/**
* Surveys CRUD API
* GET ?action=list — List surveys (filtered by role)
* GET ?action=get&id=X — Get survey with sections & questions
* GET ?action=getByLink&link=X — Get survey by unique link (public)
* POST ?action=create — Create survey with sections & questions
* POST ?action=update — Update survey
* POST ?action=delete — Delete survey
* POST ?action=generateLink — Generate new unique link
* POST ?action=updateStatus — Update survey status
*/
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../config/auth.php';
header('Content-Type: application/json; charset=utf-8');
$action = $_GET['action'] ?? '';
// Public endpoint for survey respondents
if ($action === 'getByLink') {
getByLink();
exit;
}
requireLogin();
switch ($action) {
case 'list':
listSurveys();
break;
case 'get':
getSurvey();
break;
case 'create':
createSurvey();
break;
case 'update':
updateSurvey();
break;
case 'delete':
deleteSurvey();
break;
case 'generateLink':
generateLink();
break;
case 'updateStatus':
updateStatus();
break;
case 'duplicate':
duplicateSurvey();
break;
case 'uploadTemplate':
uploadTemplate();
break;
default:
jsonResponse(['error' => 'Invalid action'], 400);
}
function listSurveys() {
$db = getDB();
if (isAdmin()) {
$stmt = $db->query("
SELECT s.*, u.full_name as creator_name,
(SELECT COUNT(*) FROM survey_responses WHERE survey_id = s.id) as response_count
FROM surveys s
LEFT JOIN users u ON s.user_id = u.id
ORDER BY s.created_at DESC
");
} else {
$stmt = $db->prepare("
SELECT s.*, u.full_name as creator_name,
(SELECT COUNT(*) FROM survey_responses WHERE survey_id = s.id) as response_count
FROM surveys s
LEFT JOIN users u ON s.user_id = u.id
WHERE s.user_id = ?
ORDER BY s.created_at DESC
");
$stmt->execute([$_SESSION['user_id']]);
}
jsonResponse(['surveys' => $stmt->fetchAll()]);
}
function getSurvey() {
$id = (int)($_GET['id'] ?? 0);
if (!$id) jsonResponse(['error' => 'Missing ID'], 400);
$db = getDB();
$stmt = $db->prepare("SELECT * FROM surveys WHERE id = ?");
$stmt->execute([$id]);
$survey = $stmt->fetch();
if (!$survey) jsonResponse(['error' => 'ไม่พบแบบสอบถาม'], 404);
// Check access
if (!isAdmin() && $survey['user_id'] != $_SESSION['user_id']) {
jsonResponse(['error' => 'ไม่มีสิทธิ์เข้าถึง'], 403);
}
// Get sections with questions
$survey['sections'] = getSections($db, $id);
jsonResponse(['survey' => $survey]);
}
function getByLink() {
$link = $_GET['link'] ?? '';
if (empty($link)) jsonResponse(['error' => 'Missing link'], 400);
$db = getDB();
$stmt = $db->prepare("SELECT * FROM surveys WHERE unique_link = ? AND status = 'active'");
$stmt->execute([$link]);
$survey = $stmt->fetch();
if (!$survey) jsonResponse(['error' => 'ไม่พบแบบสอบถามหรือแบบสอบถามถูกปิด'], 404);
$survey['sections'] = getSections($db, $survey['id']);
// Remove sensitive data
unset($survey['user_id']);
jsonResponse(['survey' => $survey]);
}
function getSections($db, $surveyId) {
$stmt = $db->prepare("SELECT * FROM survey_sections WHERE survey_id = ? ORDER BY section_number");
$stmt->execute([$surveyId]);
$sections = $stmt->fetchAll();
foreach ($sections as &$section) {
// Load headings for this section
$hStmt = $db->prepare("SELECT * FROM survey_headings WHERE section_id = ? ORDER BY order_num");
$hStmt->execute([$section['id']]);
$headings = $hStmt->fetchAll();
// Load ALL questions for this section
$qStmt = $db->prepare("SELECT * FROM survey_questions WHERE section_id = ? ORDER BY order_num");
$qStmt->execute([$section['id']]);
$allQuestions = $qStmt->fetchAll();
// Separate questions: those with heading_id vs those without
$looseQuestions = [];
$headingQuestionsMap = [];
foreach ($allQuestions as &$q) {
$q['is_required'] = (int)$q['is_required'];
if ($q['options']) {
$q['options'] = json_decode($q['options'], true);
}
if ($q['heading_id']) {
$headingQuestionsMap[$q['heading_id']][] = $q;
} else {
$looseQuestions[] = $q;
}
}
// Attach questions to their headings
foreach ($headings as &$h) {
$h['questions'] = $headingQuestionsMap[$h['id']] ?? [];
}
$section['headings'] = $headings;
$section['questions'] = $looseQuestions; // questions not in any heading
}
return $sections;
}
function insertQuestion($db, $sectionId, $headingId, $q, $orderNum) {
$options = isset($q['options']) ? json_encode($q['options'], JSON_UNESCAPED_UNICODE) : null;
$stmt = $db->prepare("INSERT INTO survey_questions (section_id, heading_id, question_text, question_type, is_required, order_num, options, scale_min, scale_max, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$sectionId,
$headingId,
$q['question_text'],
$q['question_type'] ?? 'radio',
$q['is_required'] ?? 1,
$q['order_num'] ?? $orderNum,
$options,
$q['scale_min'] ?? 1,
$q['scale_max'] ?? 5,
$q['description'] ?? null
]);
return $db->lastInsertId();
}
function createSurvey() {
$input = getInput();
$title = trim($input['title'] ?? '');
$description = trim($input['description'] ?? '');
$projectId = $input['project_id'] ?? null;
if (empty($title)) {
jsonResponse(['error' => 'กรุณากรอกชื่อแบบสอบถาม'], 400);
}
$hasCertificate = (int)($input['has_certificate'] ?? 0);
$certificateTemplate = $input['certificate_template'] ?? null;
$certNameY = isset($input['cert_name_y']) ? (double)$input['cert_name_y'] : 82.5;
$certCodeY = isset($input['cert_code_y']) ? (double)$input['cert_code_y'] : 190.5;
$db = getDB();
$uniqueLink = bin2hex(random_bytes(16));
$db->beginTransaction();
try {
$stmt = $db->prepare("INSERT INTO surveys (user_id, project_id, title, description, unique_link, has_certificate, certificate_template, cert_name_y, cert_code_y) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $projectId, $title, $description, $uniqueLink, $hasCertificate, $certificateTemplate, $certNameY, $certCodeY]);
$surveyId = $db->lastInsertId();
// Create default sections with pre-defined questions based on user requirements
$sections = $input['sections'] ?? [
[
'section_number' => 1,
'title' => 'ส่วนที่ ข้อมูลทั่วไป',
'description' => 'ข้อมูลพื้นฐานของผู้ตอบ (ต้องตอบทุกข้อ)',
'questions' => [
['question_text' => 'เพศ', 'question_type' => 'radio', 'options' => ['ชาย', 'หญิง'], 'is_required' => 1],
['question_text' => 'ช่วงอายุ', 'question_type' => 'dropdown', 'options' => ['18-25 ปี', '26-35 ปี', '36-45 ปี', '46-55 ปี', '56 ปีขึ้นไป'], 'is_required' => 1],
['question_text' => 'คณะ / หน่วยงาน', 'question_type' => 'text', 'is_required' => 1]
]
],
[
'section_number' => 2,
'title' => 'ส่วนที่ การประเมินความพึงพอใจ',
'description' => 'ประเมินระดับความพึงพอใจ (5 = มากที่สุด, 1 = น้อยที่สุด)',
'questions' => [
['question_text' => 'ด้านเนื้อหาของการจัดโครงการ', 'question_type' => 'radio', 'is_required' => 1],
['question_text' => 'ด้านวิทยากร / ผู้บรรยาย', 'question_type' => 'radio', 'is_required' => 1],
['question_text' => 'ด้านสถานที่และความสะดวก', 'question_type' => 'radio', 'is_required' => 1]
]
],
[
'section_number' => 3,
'title' => 'ส่วนที่ ข้อเสนอแนะ',
'description' => 'ข้อเสนอแนะเพิ่มเติม',
'questions' => [
['question_text' => 'ข้อเสนอแนะอื่นๆ', 'question_type' => 'text', 'is_required' => 0]
]
]
];
foreach ($sections as $section) {
$stmt = $db->prepare("INSERT INTO survey_sections (survey_id, section_number, title, description, category) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$surveyId, $section['section_number'], $section['title'], $section['description'] ?? '', $section['category'] ?? 'rating']);
$sectionId = $db->lastInsertId();
// Insert headings and their questions
if (isset($section['headings'])) {
foreach ($section['headings'] as $hi => $heading) {
$hStmt = $db->prepare("INSERT INTO survey_headings (section_id, title, description, order_num) VALUES (?, ?, ?, ?)");
$hStmt->execute([$sectionId, $heading['title'], $heading['description'] ?? null, $heading['order_num'] ?? $hi]);
$headingId = $db->lastInsertId();
if (isset($heading['questions'])) {
foreach ($heading['questions'] as $i => $q) {
insertQuestion($db, $sectionId, $headingId, $q, $i);
}
}
}
}
// Insert loose questions (not in any heading)
if (isset($section['questions'])) {
foreach ($section['questions'] as $i => $q) {
insertQuestion($db, $sectionId, null, $q, $i);
}
}
}
$db->commit();
jsonResponse(['success' => true, 'id' => $surveyId, 'unique_link' => $uniqueLink, 'message' => 'สร้างแบบสอบถามสำเร็จ'], 201);
} catch (Exception $e) {
$db->rollBack();
jsonResponse(['error' => 'เกิดข้อผิดพลาด: ' . $e->getMessage()], 500);
}
}
function updateSurvey() {
$input = getInput();
$id = (int)($input['id'] ?? 0);
if (!$id) jsonResponse(['error' => 'Missing ID'], 400);
$db = getDB();
// Check ownership
$stmt = $db->prepare("SELECT user_id FROM surveys WHERE id = ?");
$stmt->execute([$id]);
$survey = $stmt->fetch();
if (!$survey) jsonResponse(['error' => 'ไม่พบแบบสอบถาม'], 404);
if (!isAdmin() && $survey['user_id'] != $_SESSION['user_id']) {
jsonResponse(['error' => 'ไม่มีสิทธิ์'], 403);
}
$db->beginTransaction();
try {
// Update survey info
if (isset($input['title'])) {
$hasCertificate = (int)($input['has_certificate'] ?? 0);
$certificateTemplate = $input['certificate_template'] ?? null;
$certNameY = isset($input['cert_name_y']) ? (double)$input['cert_name_y'] : 82.5;
$certCodeY = isset($input['cert_code_y']) ? (double)$input['cert_code_y'] : 190.5;
$stmt = $db->prepare("UPDATE surveys SET title = ?, description = ?, has_certificate = ?, certificate_template = ?, cert_name_y = ?, cert_code_y = ? WHERE id = ?");
$stmt->execute([$input['title'], $input['description'] ?? '', $hasCertificate, $certificateTemplate, $certNameY, $certCodeY, $id]);
}
// Update sections and questions
if (isset($input['sections'])) {
// Delete old headings first (they reference sections)
$db->prepare("DELETE h FROM survey_headings h JOIN survey_sections s ON h.section_id = s.id WHERE s.survey_id = ?")->execute([$id]);
// Delete old sections (cascades to questions)
$db->prepare("DELETE FROM survey_sections WHERE survey_id = ?")->execute([$id]);
foreach ($input['sections'] as $section) {
$stmt = $db->prepare("INSERT INTO survey_sections (survey_id, section_number, title, description, category) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$id, $section['section_number'], $section['title'], $section['description'] ?? '', $section['category'] ?? 'rating']);
$sectionId = $db->lastInsertId();
// Insert headings and their questions
if (isset($section['headings'])) {
foreach ($section['headings'] as $hi => $heading) {
$hStmt = $db->prepare("INSERT INTO survey_headings (section_id, title, description, order_num) VALUES (?, ?, ?, ?)");
$hStmt->execute([$sectionId, $heading['title'], $heading['description'] ?? null, $heading['order_num'] ?? $hi]);
$headingId = $db->lastInsertId();
if (isset($heading['questions'])) {
foreach ($heading['questions'] as $i => $q) {
insertQuestion($db, $sectionId, $headingId, $q, $i);
}
}
}
}
// Insert loose questions (not in any heading)
if (isset($section['questions'])) {
foreach ($section['questions'] as $i => $q) {
insertQuestion($db, $sectionId, null, $q, $i);
}
}
}
}
$db->commit();
jsonResponse(['success' => true, 'message' => 'อัปเดตแบบสอบถามสำเร็จ']);
} catch (Exception $e) {
$db->rollBack();
jsonResponse(['error' => 'เกิดข้อผิดพลาด: ' . $e->getMessage()], 500);
}
}
function deleteSurvey() {
$input = getInput();
$id = (int)($input['id'] ?? 0);
if (!$id) jsonResponse(['error' => 'Missing ID'], 400);
$db = getDB();
$stmt = $db->prepare("SELECT user_id FROM surveys WHERE id = ?");
$stmt->execute([$id]);
$survey = $stmt->fetch();
if (!$survey) jsonResponse(['error' => 'ไม่พบแบบสอบถาม'], 404);
if (!isAdmin() && $survey['user_id'] != $_SESSION['user_id']) {
jsonResponse(['error' => 'ไม่มีสิทธิ์'], 403);
}
$db->prepare("DELETE FROM surveys WHERE id = ?")->execute([$id]);
jsonResponse(['success' => true, 'message' => 'ลบแบบสอบถามสำเร็จ']);
}
function generateLink() {
$input = getInput();
$id = (int)($input['id'] ?? 0);
if (!$id) jsonResponse(['error' => 'Missing ID'], 400);
$db = getDB();
$newLink = bin2hex(random_bytes(16));
$stmt = $db->prepare("UPDATE surveys SET unique_link = ?, status = 'active' WHERE id = ?");
$stmt->execute([$newLink, $id]);
$baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
$fullLink = $baseUrl . '/survey/respond.php?link=' . $newLink;
jsonResponse(['success' => true, 'unique_link' => $newLink, 'full_url' => $fullLink]);
}
function updateStatus() {
$input = getInput();
$id = (int)($input['id'] ?? 0);
$status = $input['status'] ?? '';
if (!$id || !in_array($status, ['draft', 'active', 'closed'])) {
jsonResponse(['error' => 'Invalid input'], 400);
}
$db = getDB();
$stmt = $db->prepare("UPDATE surveys SET status = ? WHERE id = ?");
$stmt->execute([$status, $id]);
jsonResponse(['success' => true, 'message' => 'อัปเดตสถานะสำเร็จ']);
}
function duplicateSurvey() {
$input = getInput();
$id = (int)($input['id'] ?? 0);
if (!$id) jsonResponse(['error' => 'Missing ID'], 400);
$db = getDB();
$stmt = $db->prepare("SELECT * FROM surveys WHERE id = ?");
$stmt->execute([$id]);
$survey = $stmt->fetch();
if (!$survey) jsonResponse(['error' => 'ไม่พบแบบสอบถาม'], 404);
$db->beginTransaction();
try {
$uniqueLink = bin2hex(random_bytes(16));
$stmt = $db->prepare("INSERT INTO surveys (user_id, project_id, title, description, unique_link, status, has_certificate, certificate_template, cert_name_y, cert_code_y) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$_SESSION['user_id'],
$survey['project_id'],
"สำเนาของ " . $survey['title'],
$survey['description'],
$uniqueLink,
'draft',
$survey['has_certificate'],
$survey['certificate_template'],
$survey['cert_name_y'],
$survey['cert_code_y']
]);
$newSurveyId = $db->lastInsertId();
// Copy sections
$stmt = $db->prepare("SELECT * FROM survey_sections WHERE survey_id = ? ORDER BY section_number");
$stmt->execute([$id]);
$sections = $stmt->fetchAll();
foreach ($sections as $section) {
$stmtInsert = $db->prepare("INSERT INTO survey_sections (survey_id, section_number, title, description, category) VALUES (?, ?, ?, ?, ?)");
$stmtInsert->execute([$newSurveyId, $section['section_number'], $section['title'], $section['description'], $section['category']]);
$newSectionId = $db->lastInsertId();
// Copy headings
$stmtH = $db->prepare("SELECT * FROM survey_headings WHERE section_id = ? ORDER BY order_num");
$stmtH->execute([$section['id']]);
$headings = $stmtH->fetchAll();
$headingIdMap = []; // old heading id -> new heading id
foreach ($headings as $h) {
$stmtInsertH = $db->prepare("INSERT INTO survey_headings (section_id, title, description, order_num) VALUES (?, ?, ?, ?)");
$stmtInsertH->execute([$newSectionId, $h['title'], $h['description'], $h['order_num']]);
$headingIdMap[$h['id']] = $db->lastInsertId();
}
// Copy questions
$stmtQ = $db->prepare("SELECT * FROM survey_questions WHERE section_id = ? ORDER BY order_num");
$stmtQ->execute([$section['id']]);
$questions = $stmtQ->fetchAll();
foreach ($questions as $q) {
$newHeadingId = $q['heading_id'] ? ($headingIdMap[$q['heading_id']] ?? null) : null;
$stmtInsertQ = $db->prepare("INSERT INTO survey_questions (section_id, heading_id, question_text, question_type, is_required, order_num, options, scale_min, scale_max, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmtInsertQ->execute([
$newSectionId,
$newHeadingId,
$q['question_text'],
$q['question_type'],
$q['is_required'],
$q['order_num'],
$q['options'],
$q['scale_min'],
$q['scale_max'],
$q['description']
]);
}
}
$db->commit();
jsonResponse(['success' => true, 'id' => $newSurveyId, 'message' => 'คัดลอกแบบสอบถามสำเร็จ']);
} catch (Exception $e) {
$db->rollBack();
jsonResponse(['error' => 'เกิดข้อผิดพลาด: ' . $e->getMessage()], 500);
}
}
function uploadTemplate() {
if (!isset($_FILES['template_image'])) {
jsonResponse(['error' => 'ไม่พบไฟล์ที่อัปโหลด'], 400);
}
$file = $_FILES['template_image'];
if ($file['error'] !== UPLOAD_ERR_OK) {
jsonResponse(['error' => 'การอัปโหลดไฟล์ล้มเหลว: รหัสข้อผิดพลาด ' . $file['error']], 400);
}
// ตรวจสอบขนาดไฟล์ (สูงสุด 5MB)
if ($file['size'] > 5 * 1024 * 1024) {
jsonResponse(['error' => 'ขนาดไฟล์รูปภาพต้องไม่เกิน 5MB'], 400);
}
// ตรวจสอบนามสกุล/ประเภทไฟล์ (ต้องเป็น PNG หรือ JPEG เท่านั้น)
$imageInfo = getimagesize($file['tmp_name']);
if ($imageInfo === false) {
jsonResponse(['error' => 'ไฟล์ที่อัปโหลดไม่ใช่รูปภาพที่ถูกต้อง'], 400);
}
$mimeType = $imageInfo['mime'];
$allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg'];
if (!in_array($mimeType, $allowedMimeTypes)) {
jsonResponse(['error' => 'อนุญาตให้อัปโหลดเฉพาะไฟล์รูปภาพ PNG หรือ JPG/JPEG เท่านั้น'], 400);
}
// สร้างโฟลเดอร์สำหรับเก็บไฟล์รูปภาพ (ถ้ายังไม่มี)
$uploadDir = __DIR__ . '/../uploads/templates';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
chmod($uploadDir, 0777);
// สร้างชื่อไฟล์ใหม่เพื่อป้องกันไฟล์ซ้ำและแฮกเกอร์
$ext = ($mimeType === 'image/png') ? 'png' : 'jpg';
$fileName = 'tpl_' . time() . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
$destPath = $uploadDir . '/' . $fileName;
if (move_uploaded_file($file['tmp_name'], $destPath)) {
chmod($destPath, 0666);
$relativePath = 'uploads/templates/' . $fileName;
jsonResponse(['success' => true, 'template_path' => $relativePath, 'message' => 'อัปโหลดเทมเพลตสำเร็จ']);
} else {
jsonResponse(['error' => 'ไม่สามารถย้ายไฟล์ไปยังโฟลเดอร์ปลายทางได้'], 500);
}
}