Kotchasan Framework Documentation
การจัดการ Input และ Request
การจัดการ Input และ Request
Kotchasan Framework มีระบบจัดการ Input และ Request ที่ครอบคลุมและทันสมัย รองรับ PSR-7 HTTP Message Interface พร้อมฟีเจอร์เพิ่มเติมสำหรับการพัฒนาเว็บแอปพลิเคชันที่ปลอดภัยและมีประสิทธิภาพ
สารบัญ
- Request Class - การจัดการ HTTP Request
- Input Class - การประมวลผลข้อมูล Input
- InputItem Class - การจัดการข้อมูลรายตัว
- Response Class - การสร้าง HTTP Response
- การจัดการพารามิเตอร์และการตรวจสอบ
- การอัปโหลดไฟล์
- การจัดรูปแบบการตอบกลับ
- ตัวอย่างการใช้งานในระบบจริง
Request Class - การจัดการ HTTP Request
การสร้าง Request Object
use Kotchasan\Http\Request;
// สร้าง Request จากตัวแปรระบบ
$request = new Request();
// สร้าง Request โดยไม่รวมข้อมูล superglobals
$request = new Request(false);
// ใช้ Request ในลักษณะ PSR-7
$newRequest = $request->withQueryParams(['page' => 2]);
$anotherRequest = $request->withParsedBody(['name' => 'John']);การดึงข้อมูล HTTP Method และข้อมูลพื้นฐาน
// ตรวจสอบ HTTP Method
$method = $request->getMethod(); // GET, POST, PUT, DELETE, etc.
// Method Override Support (สำหรับ form ที่ไม่รองรับ PUT/DELETE)
// 1. ผ่าน header X-HTTP-Method-Override
// 2. ผ่าน parameter _method ใน POST
$isSecure = $request->isSecure(); // true หาก HTTPS
$protocol = $request->getProtocolVersion(); // 1.0, 1.1, 2.0
$uri = $request->getUri(); // URI object
// ตรวจสอบประเภท Request
$isAjax = $request->isAjax();
$isJson = $request->isJson();
$isGet = $request->isMethod('GET');
$isPost = $request->isMethod('POST');การดึงข้อมูลพารามิเตอร์
// การดึงข้อมูล GET parameters
$id = $request->get('id'); // Returns InputItem
$page = $request->get('page', 1); // ค่าเริ่มต้น 1
// การดึงข้อมูล POST parameters
$name = $request->post('name');
$email = $request->post('email', '');
// การดึงข้อมูลแบบรวม (POST -> GET -> Cookie)
$value = $request->request('key', 'default_value');
// การดึงข้อมูล raw (ไม่ผ่าน wrapper)
$rawValue = $request->postRaw('data');
$rawGet = $request->getRaw('param');
// การดึงข้อมูล Server parameters
$userAgent = $request->server('HTTP_USER_AGENT');
$contentType = $request->server('CONTENT_TYPE');
$remoteAddr = $request->server('REMOTE_ADDR');การจัดการ Headers
// ดึง Headers
$allHeaders = $request->getHeaders();
$contentType = $request->getHeaderLine('Content-Type');
$authorization = $request->getHeaderLine('Authorization');
// ตรวจสอบการมีอยู่ของ Header
if ($request->hasHeader('X-Requested-With')) {
echo "This is an AJAX request";
}
// เพิ่ม/แก้ไข Headers (PSR-7 immutable)
$newRequest = $request->withHeader('Accept', 'application/json');
$requestWithHeaders = $request->withHeaders([
'Accept' => 'application/json',
'X-Custom-Header' => 'custom-value'
]);การจัดการ Body และ JSON
// ดึง Request Body
$body = $request->getBody();
$bodyContent = $body->getContents();
// ดึงข้อมูล JSON
if ($request->isJson()) {
$jsonData = $request->getJson();
$specificValue = $request->getJsonData('user.name', 'Unknown');
}
// ดึงข้อมูล Parsed Body
$parsedBody = $request->getParsedBody(); // array หรือ object
// ตรวจสอบขนาด Content
$contentLength = $request->getContentLength();
if ($contentLength > 1024 * 1024) { // 1MB
throw new Exception('Request too large');
}Input Class - การประมวลผลข้อมูล Input
การใช้งาน Input Class
use Kotchasan\Input;
// สร้าง Input instance
$input = new Input($request);
// หรือใช้ static method
$input = Input::create($request);
// การใช้งานแบบ immutable (PSR-7)
$newInput = $input->withRequest($newRequest);
$inputWithQuery = $input->withQueryParams(['sort' => 'name']);
$inputWithBody = $input->withParsedBody(['action' => 'save']);การดึงข้อมูลจากแหล่งต่างๆ
// ดึงข้อมูลจากแหล่งเฉพาะ
$postData = $input->get('username', '', 'post');
$getData = $input->get('page', 1, 'get');
$cookieData = $input->get('session_id', '', 'cookie');
$serverData = $input->get('HTTP_HOST', '', 'server');
$jsonData = $input->get('data', [], 'json');
// ดึงข้อมูลจากทุกแหล่ง (ลำดับ: POST -> GET -> JSON)
$value = $input->get('key', 'default'); // สำหรับ 'all' source
// ดึงข้อมูลทั้งหมดจากแหล่งเฉพาะ
$allPost = $input->all('post');
$allGet = $input->all('get');
$allCookies = $input->all('cookie');
$allServer = $input->all('server');
$allJson = $input->all('json');
$allData = $input->all(); // รวมทั้งหมด;การใช้งานข้อมูลแบบ Type-safe
// การดึงข้อมูลพร้อมการแปลงประเภท
$userId = $input->getInt('user_id', 0);
$price = $input->getFloat('price', 0.0);
$isActive = $input->getBool('active', false);
$tags = $input->getArray('tags', []);
$description = $input->getString('description', '');
// การดึงข้อมูลประเภทพิเศษ
$email = $input->getEmail('email');
$url = $input->getUrl('website');
$phone = $input->getPhone('phone');
$date = $input->getDate('birth_date');
$time = $input->getTime('appointment_time');
// การดึงข้อมูล nested จาก JSON
$cityName = $input->getNestedValue('address.city', 'Unknown');
$coordinates = $input->getNestedArray('location.coordinates', []);การตรวจสอบความปลอดภัย
// การทำความสะอาดข้อมูล
$cleanInput = $input->sanitize($userInput);
$cleanArray = $input->sanitizeArray($userArray);
// การตรวจสอบ CSRF Token
$token = $input->getString('csrf_token');
if (!$input->validateCsrfToken($token)) {
throw new SecurityException('Invalid CSRF token');
}
// การสร้าง CSRF Token
$csrfToken = $input->generateCsrfToken();
// การตรวจสอบ Rate Limiting
if (!$input->checkRateLimit('login', 5, 300)) { // 5 attempts in 5 minutes
throw new Exception('Too many attempts');
}InputItem Class - การจัดการข้อมูลรายตัว
การใช้งาน InputItem
// InputItem จะถูกสร้างโดยอัตโนมัติเมื่อใช้ get() methods
$username = $request->get('username'); // Returns InputItem
// การแปลงประเภทข้อมูล
$asString = $username->toString();
$asInt = $username->toInt();
$asFloat = $username->toFloat();
$asBool = $username->toBool();
$asArray = $username->toArray();
// การตรวจสอบและทำความสะอาด
$filtered = $username->filter(); // กรองข้อมูลอันตราย
$alphanumeric = $username->alphanumeric(); // เฉพาะตัวอักษรและตัวเลข
$oneLine = $username->oneLine(); // ลบ newline และ tab
$truncated = $username->cut(50); // ตัดให้เหลือ 50 ตัวอักษร
// การตรวจสอบรูปแบบ
$cleanEmail = $username->email(); // ตรวจสอบรูปแบบ email
$cleanUrl = $username->url(); // ตรวจสอบรูปแบบ URL
$cleanPhone = $username->phone(); // ตรวจสอบรูปแบบเบอร์โทร
$cleanDate = $username->date(); // ตรวจสอบรูปแบบวันที่
// การประมวลผลข้อความ
$topic = $username->topic(); // สำหรับหัวข้อ
$detail = $username->detail(); // สำหรับรายละเอียด
$description = $username->description(); // สำหรับคำอธิบาย
$keywords = $username->keywords(); // สำหรับคีย์เวิร์ด
$textarea = $username->textarea(); // สำหรับ textarea
// การตรวจสอบการมีอยู่
$exists = $username->exists(); // ตรวจสอบว่ามีค่าหรือไม่
$isEmpty = $username->isEmpty(); // ตรวจสอบว่าว่างหรือไม่;การใช้งานแบบ Fluent Interface
// Chain methods สำหรับการประมวลผลข้อมูล
$processedValue = $request->get('user_input')
->filter() // กรองข้อมูลอันตราย
->oneLine() // ลบ newline
->cut(100) // ตัดเหลือ 100 ตัวอักษร
->toString(); // แปลงเป็น string
$cleanNumber = $request->get('amount')
->number() // ตรวจสอบว่าเป็นตัวเลข
->toFloat(); // แปลงเป็น float
$validatedData = $request->get('data')
->json() // ตรวจสอบ JSON format
->toArray(); // แปลงเป็น array;Response Class - การสร้าง HTTP Response
การสร้าง Response พื้นฐาน
use Kotchasan\Http\Response;
// สร้าง Response ใหม่
$response = new Response();
// ตั้งค่า Status Code
$response = $response->withStatus(200); // OK
$response = $response->withStatus(404, 'Not Found');
$response = $response->withStatus(500, 'Internal Server Error');
// การตั้งค่า Headers
$response = $response->withHeader('Content-Type', 'application/json');
$response = $response->withHeaders([
'Cache-Control' => 'no-cache',
'X-Frame-Options' => 'DENY'
]);
// การตั้งค่า Content
$response = $response->withContent('Hello World');
$content = $response->getContent();การสร้าง JSON Response
// JSON Response พื้นฐาน
$data = ['message' => 'Success', 'data' => $userData];
$jsonResponse = $response->json($data, 200);
// Static methods สำหรับ JSON
$successResponse = Response::makeJson(['success' => true], 200);
$errorResponse = Response::makeJson(['error' => 'Not found'], 404);
// Predefined JSON responses
$okResponse = Response::makeOk(['data' => $data]);
$createdResponse = Response::makeCreated(['id' => $newId]);
$badRequestResponse = Response::makeBadRequest(['error' => 'Invalid input']);
$unauthorizedResponse = Response::makeUnauthorized(['error' => 'Login required']);
$forbiddenResponse = Response::makeForbidden(['error' => 'Access denied']);
$notFoundResponse = Response::makeNotFound(['error' => 'Resource not found']);
$serverErrorResponse = Response::makeServerError(['error' => 'Internal error']);การสร้าง HTML Response
// HTML Response
$htmlContent = '<html><body><h1>Welcome</h1></body></html>';
$htmlResponse = $response->html($htmlContent, 200);
// HTML Response พร้อม template
$templateData = ['title' => 'Home', 'content' => 'Welcome'];
$renderedHtml = $view->render('home', $templateData);
$htmlResponse = $response->html($renderedHtml);การสร้าง Redirect Response
// Redirect responses
$redirectResponse = $response->redirect('/new-location'); // 302 Found
$permanentRedirect = $response->redirect('/new-location', 301); // 301 Moved Permanently
// Redirect พร้อม flash message
$redirectWithMessage = $response->redirect('/dashboard')
->withHeader('X-Flash-Message', 'Login successful');การตั้งค่า Security Headers
// ตั้งค่า Security headers พื้นฐาน
$secureResponse = $response->setSecurityHeaders();
// หรือตั้งค่าเอง
$secureResponse = $response->withHeaders([
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'DENY',
'X-XSS-Protection' => '1; mode=block',
'Content-Security-Policy' => "default-src 'self'",
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains'
]);
// ตั้งค่า CORS headers
$corsResponse = $response->setCorsHeaders();
// หรือตั้งค่า CORS แบบกำหนดเอง
$corsResponse = $response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
]);
// ตั้งค่า No-cache headers
$noCacheResponse = $response->setNoCacheHeaders();การสร้าง File Download Response
// File download
$fileContent = file_get_contents('/path/to/file.pdf');
$downloadResponse = $response->download($fileContent, 'document.pdf', 'application/pdf');
// File stream (สำหรับไฟล์ขนาดใหญ่)
$filePath = '/path/to/large-file.zip';
$fileResponse = $response->file($filePath, 'archive.zip');
// Image response
$imageContent = file_get_contents('/path/to/image.jpg');
$imageResponse = $response->withContent($imageContent)
->withHeader('Content-Type', 'image/jpeg')
->withHeader('Content-Disposition', 'inline; filename="image.jpg"');การจัดการพารามิเตอร์และการตรวจสอบ
การตรวจสอบและ Validate พารามิเตอร์
class UserController
{
public function updateProfile(Request $request): Response
{
$input = new Input($request);
// การตรวจสอบพารามิเตอร์ที่จำเป็น
$requiredParams = ['name', 'email'];
foreach ($requiredParams as $param) {
if (!$input->get($param)->exists()) {
return Response::makeBadRequest([
'error' => "Missing required parameter: {$param}"
]);
}
}
// การตรวจสอบรูปแบบข้อมูล
$name = $input->getString('name');
if (strlen($name) < 2) {
return Response::makeBadRequest([
'error' => 'Name must be at least 2 characters'
]);
}
$email = $input->getEmail('email');
if (empty($email)) {
return Response::makeBadRequest([
'error' => 'Invalid email format'
]);
}
// ตรวจสอบ CSRF token
if (!$input->validateCsrfToken($input->getString('csrf_token'))) {
return Response::makeForbidden(['error' => 'Invalid CSRF token']);
}
// ประมวลผลข้อมูล
$userData = [
'name' => $name,
'email' => $email,
'phone' => $input->getPhone('phone'),
'birth_date' => $input->getDate('birth_date'),
'is_active' => $input->getBool('is_active', true)
];
// บันทึกข้อมูล
$this->userService->updateProfile($userData);
return Response::makeOk(['message' => 'Profile updated successfully']);
}
}การจัดการ Pagination
class ProductController
{
public function getProducts(Request $request): Response
{
$input = new Input($request);
// การจัดการ pagination parameters
$page = max(1, $input->getInt('page', 1));
$limit = min(100, max(10, $input->getInt('limit', 20))); // จำกัด 10-100
$sort = $input->getString('sort', 'created_at');
$order = in_array($input->getString('order'), ['asc', 'desc'])
? $input->getString('order')
: 'desc';
// การกรองข้อมูล
$filters = [];
if ($input->get('category')->exists()) {
$filters['category_id'] = $input->getInt('category');
}
if ($input->get('search')->exists()) {
$filters['search'] = $input->getString('search');
}
if ($input->get('price_min')->exists()) {
$filters['price_min'] = $input->getFloat('price_min');
}
if ($input->get('price_max')->exists()) {
$filters['price_max'] = $input->getFloat('price_max');
}
// ดึงข้อมูล
$offset = ($page - 1) $limit;
$products = $this->productService->getProducts($filters, $sort, $order, $limit, $offset);
$total = $this->productService->countProducts($filters);
// สร้าง pagination metadata
$pagination = [
'current_page' => $page,
'per_page' => $limit,
'total' => $total,
'total_pages' => ceil($total / $limit),
'has_more' => ($page $limit) < $total
];
return Response::makeOk([
'products' => $products,
'pagination' => $pagination,
'filters' => $filters
]);
}
}การอัปโหลดไฟล์
การจัดการไฟล์อัปโหลดพื้นฐาน
class FileUploadController
{
public function uploadAvatar(Request $request): Response
{
$input = new Input($request);
// ตรวจสอบไฟล์อัปโหลด
$uploadedFiles = $request->getUploadedFiles();
if (!isset($uploadedFiles['avatar'])) {
return Response::makeBadRequest(['error' => 'No file uploaded']);
}
$file = $uploadedFiles['avatar'];
// ตรวจสอบ error
if ($file->getError() !== UPLOAD_ERR_OK) {
return Response::makeBadRequest(['error' => 'File upload failed']);
}
// ตรวจสอบขนาดไฟล์
$maxSize = 2; // 1024 1024; // 2MB
if ($file->getSize() > $maxSize) {
return Response::makeBadRequest(['error' => 'File too large']);
}
// ตรวจสอบประเภทไฟล์
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$mimeType = $file->getClientMediaType();
if (!in_array($mimeType, $allowedTypes)) {
return Response::makeBadRequest(['error' => 'Invalid file type']);
}
// ตรวจสอบความปลอดภัยไฟล์
if (!Validator::isFileSafe($file)) {
return Response::makeBadRequest(['error' => 'File contains malicious content']);
}
// บันทึกไฟล์
$filename = uniqid() . '_' . $file->getClientFilename();
$uploadPath = '/uploads/avatars/' . $filename;
$file->moveTo($uploadPath);
// บันทึกข้อมูลในฐานข้อมูล
$fileInfo = [
'original_name' => $file->getClientFilename(),
'filename' => $filename,
'path' => $uploadPath,
'size' => $file->getSize(),
'mime_type' => $mimeType,
'uploaded_at' => date('Y-m-d H:i:s')
];
$fileId = $this->fileService->saveFileInfo($fileInfo);
return Response::makeCreated([
'file_id' => $fileId,
'url' => '/uploads/avatars/' . $filename,
'message' => 'File uploaded successfully'
]);
}
}การอัปโหลดไฟล์หลายไฟล์
class MultiFileUploadController
{
public function uploadDocuments(Request $request): Response
{
$input = new Input($request);
$uploadedFiles = $request->getUploadedFiles();
if (!isset($uploadedFiles['documents']) || !is_array($uploadedFiles['documents'])) {
return Response::makeBadRequest(['error' => 'No files uploaded']);
}
$files = $uploadedFiles['documents'];
$uploadedFileInfo = [];
$errors = [];
foreach ($files as $index => $file) {
try {
// ตรวจสอบแต่ละไฟล์
if ($file->getError() !== UPLOAD_ERR_OK) {
$errors[] = "File {$index}: Upload failed";
continue;
}
// ตรวจสอบขนาด
if ($file->getSize() > 10 1024 1024) { // 10MB
$errors[] = "File {$index}: Too large";
continue;
}
// ตรวจสอบประเภท
$allowedTypes = ['application/pdf', 'application/msword', 'text/plain'];
if (!in_array($file->getClientMediaType(), $allowedTypes)) {
$errors[] = "File {$index}: Invalid type";
continue;
}
// บันทึกไฟล์
$filename = uniqid() . '_' . $file->getClientFilename();
$uploadPath = '/uploads/documents/' . $filename;
$file->moveTo($uploadPath);
$uploadedFileInfo[] = [
'original_name' => $file->getClientFilename(),
'filename' => $filename,
'path' => $uploadPath,
'size' => $file->getSize(),
'mime_type' => $file->getClientMediaType()
];
} catch (Exception $e) {
$errors[] = "File {$index}: {$e->getMessage()}";
}
}
if (empty($uploadedFileInfo) && !empty($errors)) {
return Response::makeBadRequest([
'error' => 'No files were uploaded successfully',
'details' => $errors
]);
}
return Response::makeOk([
'uploaded_files' => $uploadedFileInfo,
'errors' => $errors,
'message' => count($uploadedFileInfo) . ' files uploaded successfully'
]);
}
}การจัดรูปแบบการตอบกลับ
API Response Format Standards
class ApiResponseFormatter
{
/
สร้าง Success Response แบบมาตรฐาน
/
public static function success($data = null, string $message = 'Success', int $status = 200): Response
{
$response = [
'success' => true,
'message' => $message,
'timestamp' => date('c'),
'request_id' => uniqid()
];
if ($data !== null) {
$response['data'] = $data;
}
return Response::makeJson($response, $status);
}
/
สร้าง Error Response แบบมาตรฐาน
/
public static function error(string $message, int $status = 400, array $errors = [], $code = null): Response
{
$response = [
'success' => false,
'message' => $message,
'timestamp' => date('c'),
'request_id' => uniqid()
];
if (!empty($errors)) {
$response['errors'] = $errors;
}
if ($code !== null) {
$response['error_code'] = $code;
}
return Response::makeJson($response, $status);
}
/
สร้าง Paginated Response
/
public static function paginated(array $items, int $total, int $page, int $limit, string $message = 'Data retrieved'): Response
{
$totalPages = ceil($total / $limit);
$data = [
'items' => $items,
'pagination' => [
'current_page' => $page,
'per_page' => $limit,
'total_items' => $total,
'total_pages' => $totalPages,
'has_next' => $page < $totalPages,
'has_prev' => $page > 1,
'next_page' => $page < $totalPages ? $page + 1 : null,
'prev_page' => $page > 1 ? $page - 1 : null
]
];
return self::success($data, $message);
}
/
สร้าง Validation Error Response
/
public static function validationError(array $errors, string $message = 'Validation failed'): Response
{
return self::error($message, 422, $errors, 'VALIDATION_ERROR');
}
}
// การใช้งาน
class UserApiController
{
public function getUsers(Request $request): Response
{
$input = new Input($request);
try {
$page = $input->getInt('page', 1);
$limit = $input->getInt('limit', 20);
$users = $this->userService->getUsers($page, $limit);
$total = $this->userService->getTotalUsers();
return ApiResponseFormatter::paginated($users, $total, $page, $limit);
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to retrieve users', 500, [], 'SERVER_ERROR');
}
}
public function createUser(Request $request): Response
{
$input = new Input($request);
// Validation
$validation = $this->validateUserInput($input);
if (!empty($validation['errors'])) {
return ApiResponseFormatter::validationError($validation['errors']);
}
try {
$user = $this->userService->createUser($validation['data']);
return ApiResponseFormatter::success($user, 'User created successfully', 201);
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to create user', 500);
}
}
}Content Negotiation
class ContentNegotiationController
{
public function getResource(Request $request): Response
{
$input = new Input($request);
$acceptHeader = $request->getHeaderLine('Accept');
// ดึงข้อมูล
$data = $this->resourceService->getData();
// การตัดสินใจรูปแบบการตอบกลับตาม Accept header
if (strpos($acceptHeader, 'application/json') !== false) {
return Response::makeJson($data);
}
elseif (strpos($acceptHeader, 'application/xml') !== false) {
$xml = $this->convertToXml($data);
return (new Response())
->withContent($xml)
->withHeader('Content-Type', 'application/xml');
}
elseif (strpos($acceptHeader, 'text/csv') !== false) {
$csv = $this->convertToCsv($data);
return (new Response())
->withContent($csv)
->withHeader('Content-Type', 'text/csv')
->withHeader('Content-Disposition', 'attachment; filename="data.csv"');
}
else {
// Default to HTML
$html = $this->renderHtml($data);
return (new Response())->html($html);
}
}
private function convertToXml(array $data): string
{
$xml = new SimpleXMLElement('<root/>');
array_walk_recursive($data, function($value, $key) use ($xml) {
$xml->addChild($key, htmlspecialchars($value));
});
return $xml->asXML();
}
private function convertToCsv(array $data): string
{
$output = fopen('php://temp', 'w');
if (!empty($data)) {
// Write header
fputcsv($output, array_keys($data[0]));
// Write data
foreach ($data as $row) {
fputcsv($output, $row);
}
}
rewind($output);
$csv = stream_get_contents($output);
fclose($output);
return $csv;
}
}ตัวอย่างการใช้งานในระบบจริง
ระบบ REST API สำหรับจัดการผู้ใช้
class UserRestController
{
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
/
GET /api/users - ดึงรายชื่อผู้ใช้
/
public function index(Request $request): Response
{
$input = new Input($request);
// การจัดการ query parameters
$filters = [
'search' => $input->getString('search', ''),
'role' => $input->getString('role', ''),
'status' => $input->getString('status', 'active'),
'created_from' => $input->getDate('created_from'),
'created_to' => $input->getDate('created_to')
];
$page = max(1, $input->getInt('page', 1));
$limit = min(100, max(10, $input->getInt('limit', 20)));
$sort = $input->getString('sort', 'created_at');
$order = in_array($input->getString('order'), ['asc', 'desc']) ? $input->getString('order') : 'desc';
try {
$users = $this->userService->getUsers($filters, $sort, $order, $limit, $page);
$total = $this->userService->countUsers($filters);
return ApiResponseFormatter::paginated($users, $total, $page, $limit);
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to retrieve users', 500);
}
}
/
GET /api/users/{id} - ดึงข้อมูลผู้ใช้รายคน
/
public function show(Request $request, int $id): Response
{
try {
$user = $this->userService->getUserById($id);
if (!$user) {
return ApiResponseFormatter::error('User not found', 404, [], 'USER_NOT_FOUND');
}
return ApiResponseFormatter::success($user, 'User retrieved successfully');
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to retrieve user', 500);
}
}
/
POST /api/users - สร้างผู้ใช้ใหม่
/
public function store(Request $request): Response
{
$input = new Input($request);
// ตรวจสอบ CSRF token
if (!$input->validateCsrfToken($input->getString('csrf_token'))) {
return ApiResponseFormatter::error('Invalid CSRF token', 403, [], 'CSRF_ERROR');
}
// Validation rules
$rules = [
'username' => 'required|min:3|max:20|alphanumeric',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|password_strength',
'first_name' => 'required|min:2|max:50',
'last_name' => 'required|min:2|max:50',
'role' => 'required|in:admin,user,moderator'
];
$validation = $this->validateInput($input, $rules);
if (!$validation['valid']) {
return ApiResponseFormatter::validationError($validation['errors']);
}
try {
$userData = [
'username' => $input->getString('username'),
'email' => $input->getEmail('email'),
'password' => password_hash($input->getString('password'), PASSWORD_DEFAULT),
'first_name' => $input->getString('first_name'),
'last_name' => $input->getString('last_name'),
'role' => $input->getString('role'),
'created_at' => date('Y-m-d H:i:s')
];
$user = $this->userService->createUser($userData);
return ApiResponseFormatter::success($user, 'User created successfully', 201);
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to create user', 500);
}
}
/
PUT /api/users/{id} - อัพเดทข้อมูลผู้ใช้
/
public function update(Request $request, int $id): Response
{
$input = new Input($request);
// ตรวจสอบว่าผู้ใช้มีอยู่
$existingUser = $this->userService->getUserById($id);
if (!$existingUser) {
return ApiResponseFormatter::error('User not found', 404, [], 'USER_NOT_FOUND');
}
// ตรวจสอบสิทธิ์การแก้ไข
if (!$this->canEditUser($request, $id)) {
return ApiResponseFormatter::error('Access denied', 403, [], 'ACCESS_DENIED');
}
// Validation (บางฟิลด์เป็น optional สำหรับ update)
$rules = [
'email' => 'email|unique:users,email,' . $id,
'first_name' => 'min:2|max:50',
'last_name' => 'min:2|max:50',
'role' => 'in:admin,user,moderator'
];
$validation = $this->validateInput($input, $rules);
if (!$validation['valid']) {
return ApiResponseFormatter::validationError($validation['errors']);
}
try {
$updateData = [];
// อัพเดทเฉพาะฟิลด์ที่ส่งมา
if ($input->get('email')->exists()) {
$updateData['email'] = $input->getEmail('email');
}
if ($input->get('first_name')->exists()) {
$updateData['first_name'] = $input->getString('first_name');
}
if ($input->get('last_name')->exists()) {
$updateData['last_name'] = $input->getString('last_name');
}
if ($input->get('role')->exists()) {
$updateData['role'] = $input->getString('role');
}
// อัพเดทรหัสผ่านถ้ามี
if ($input->get('password')->exists()) {
$password = $input->getString('password');
if (strlen($password) >= 8) {
$updateData['password'] = password_hash($password, PASSWORD_DEFAULT);
}
}
$updateData['updated_at'] = date('Y-m-d H:i:s');
$updatedUser = $this->userService->updateUser($id, $updateData);
return ApiResponseFormatter::success($updatedUser, 'User updated successfully');
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to update user', 500);
}
}
/
DELETE /api/users/{id} - ลบผู้ใช้
/
public function destroy(Request $request, int $id): Response
{
// ตรวจสอบว่าผู้ใช้มีอยู่
$user = $this->userService->getUserById($id);
if (!$user) {
return ApiResponseFormatter::error('User not found', 404, [], 'USER_NOT_FOUND');
}
// ตรวจสอบสิทธิ์การลบ
if (!$this->canDeleteUser($request, $id)) {
return ApiResponseFormatter::error('Access denied', 403, [], 'ACCESS_DENIED');
}
try {
$this->userService->deleteUser($id);
return ApiResponseFormatter::success(null, 'User deleted successfully');
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to delete user', 500);
}
}
private function validateInput(Input $input, array $rules): array
{
$input->rules($rules);
$valid = $input->validate();
return [
'valid' => $valid,
'errors' => $valid ? [] : $input->errors(),
'data' => $valid ? $input->validated() : []
];
}
private function canEditUser(Request $request, int $userId): bool
{
// ตรวจสอบสิทธิ์จาก JWT token หรือ session
$currentUserId = $this->getCurrentUserId($request);
$currentUserRole = $this->getCurrentUserRole($request);
// Admin สามารถแก้ไขได้ทุกคน, user แก้ไขได้เฉพาะตัวเอง
return $currentUserRole === 'admin' || $currentUserId === $userId;
}
private function canDeleteUser(Request $request, int $userId): bool
{
$currentUserRole = $this->getCurrentUserRole($request);
return $currentUserRole === 'admin';
}
}ระบบอัปโหลดและจัดการไฟล์
class FileManagementController
{
/
POST /api/files/upload - อัปโหลดไฟล์
/
public function upload(Request $request): Response
{
$input = new Input($request);
// ตรวจสอบสิทธิ์การอัปโหลด
if (!$this->canUpload($request)) {
return ApiResponseFormatter::error('Upload permission denied', 403);
}
$uploadedFiles = $request->getUploadedFiles();
if (empty($uploadedFiles['file'])) {
return ApiResponseFormatter::error('No file uploaded', 400);
}
$file = $uploadedFiles['file'];
// ตรวจสอบไฟล์
$validation = $this->validateFile($file, $input);
if (!$validation['valid']) {
return ApiResponseFormatter::validationError($validation['errors']);
}
try {
// สร้าง unique filename
$extension = pathinfo($file->getClientFilename(), PATHINFO_EXTENSION);
$filename = uniqid() . '_' . time() . '.' . $extension;
// กำหนด upload path ตามประเภทไฟล์
$uploadDir = $this->getUploadDirectory($file->getClientMediaType());
$fullPath = $uploadDir . '/' . $filename;
// ย้ายไฟล์
$file->moveTo($fullPath);
// บันทึกข้อมูลในฐานข้อมูล
$fileData = [
'original_name' => $file->getClientFilename(),
'filename' => $filename,
'path' => $fullPath,
'size' => $file->getSize(),
'mime_type' => $file->getClientMediaType(),
'uploaded_by' => $this->getCurrentUserId($request),
'upload_ip' => $request->server('REMOTE_ADDR'),
'created_at' => date('Y-m-d H:i:s')
];
$fileId = $this->fileService->saveFile($fileData);
// สร้าง thumbnail สำหรับรูปภาพ
if (strpos($file->getClientMediaType(), 'image/') === 0) {
$this->createThumbnail($fullPath, $filename);
}
return ApiResponseFormatter::success([
'file_id' => $fileId,
'filename' => $filename,
'original_name' => $file->getClientFilename(),
'size' => $file->getSize(),
'url' => $this->getFileUrl($filename),
'thumbnail_url' => $this->getThumbnailUrl($filename)
], 'File uploaded successfully', 201);
} catch (Exception $e) {
return ApiResponseFormatter::error('Upload failed: ' . $e->getMessage(), 500);
}
}
/
GET /api/files/{id}/download - ดาวน์โหลดไฟล์
/
public function download(Request $request, int $fileId): Response
{
try {
$file = $this->fileService->getFileById($fileId);
if (!$file) {
return ApiResponseFormatter::error('File not found', 404);
}
// ตรวจสอบสิทธิ์การดาวน์โหลด
if (!$this->canDownload($request, $file)) {
return ApiResponseFormatter::error('Download permission denied', 403);
}
// ตรวจสอบไฟล์จริงบนระบบ
if (!file_exists($file['path'])) {
return ApiResponseFormatter::error('File not found on disk', 404);
}
// อัพเดท download count
$this->fileService->incrementDownloadCount($fileId);
// ส่งไฟล์
$response = new Response();
return $response->file($file['path'], $file['original_name']);
} catch (Exception $e) {
return ApiResponseFormatter::error('Download failed', 500);
}
}
/
GET /api/files - รายการไฟล์
/
public function index(Request $request): Response
{
$input = new Input($request);
$filters = [
'mime_type' => $input->getString('type', ''),
'search' => $input->getString('search', ''),
'uploaded_by' => $input->getInt('user_id', 0),
'date_from' => $input->getDate('date_from'),
'date_to' => $input->getDate('date_to')
];
$page = max(1, $input->getInt('page', 1));
$limit = min(50, max(10, $input->getInt('limit', 20)));
try {
$files = $this->fileService->getFiles($filters, $page, $limit);
$total = $this->fileService->countFiles($filters);
// เพิ่มข้อมูล URL สำหรับแต่ละไฟล์
$filesWithUrls = array_map(function($file) {
$file['url'] = $this->getFileUrl($file['filename']);
$file['thumbnail_url'] = $this->getThumbnailUrl($file['filename']);
return $file;
}, $files);
return ApiResponseFormatter::paginated($filesWithUrls, $total, $page, $limit);
} catch (Exception $e) {
return ApiResponseFormatter::error('Failed to retrieve files', 500);
}
}
private function validateFile($file, Input $input): array
{
$errors = [];
// ตรวจสอบ upload error
if ($file->getError() !== UPLOAD_ERR_OK) {
$errors[] = 'File upload failed with error code: ' . $file->getError();
return ['valid' => false, 'errors' => $errors];
}
// ตรวจสอบขนาดไฟล์
$maxSize = $this->getMaxFileSize($file->getClientMediaType());
if ($file->getSize() > $maxSize) {
$errors[] = 'File size exceeds limit of ' . ($maxSize / 1024 / 1024) . 'MB';
}
// ตรวจสอบประเภทไฟล์
$allowedTypes = $this->getAllowedFileTypes();
if (!in_array($file->getClientMediaType(), $allowedTypes)) {
$errors[] = 'File type not allowed: ' . $file->getClientMediaType();
}
// ตรวจสอบความปลอดภัย
if (!Validator::isFileSafe($file)) {
$errors[] = 'File contains potentially malicious content';
}
// ตรวจสอบขนาดรูปภาพ (ถ้าเป็นรูปภาพ)
if (strpos($file->getClientMediaType(), 'image/') === 0) {
$imageInfo = getimagesize($file->getStream()->getMetadata('uri'));
if ($imageInfo) {
$maxWidth = 4000;
$maxHeight = 4000;
if ($imageInfo[0] > $maxWidth || $imageInfo[1] > $maxHeight) {
$errors[] = "Image dimensions too large. Max: {$maxWidth}x{$maxHeight}";
}
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
}ระบบ Input และ Request ของ Kotchasan Framework มีความสมบูรณ์และทันสมัย รองรับการพัฒนาแอปพลิเคชันเว็บที่ปลอดภัยและมีประสิทธิภาพสูง ด้วยการรองรับ PSR-7 และฟีเจอร์เพิ่มเติมที่จำเป็นสำหรับการใช้งานจริง