File manager - Edit - /home/webapp69.cm.in.th/u69319090041/Shop41/public/add-product.php
Back
<?php /** * public/add-product.php — หน้าเพิ่มสินค้าใหม่ (Add Product Form) พร้อมระบบพิมพ์สร้างหมวดหมู่ใหม่ได้เอง */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require_once __DIR__ . '/../config/config.php'; require_once __DIR__ . '/../config/database.php'; require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/image_helper.php'; // ตรวจสอบการเข้าสู่ระบบ requireLogin(); $db = getDBConnection(); $userId = $_SESSION['user']['id'] ?? $_SESSION['user_id'] ?? 0; // ดึงข้อมูลร้านค้าของผู้ใช้งานนี้ $vendor = null; try { $stmt = $db->prepare("SELECT * FROM vendors WHERE user_id = ? AND status = 'approved'"); $stmt->execute([$userId]); $vendor = $stmt->fetch(); } catch (PDOException $e) {} // หากไม่ใช่ Admin และยังไม่มีร้านค้าหรือร้านค้ายังไม่อนุมัติ if (!isAdmin() && !$vendor) { $_SESSION['flash_error'] = "คุณต้องมีร้านค้าที่ได้รับการอนุมัติก่อนจึงจะสามารถเพิ่มสินค้าได้"; header("Location: register_store.php"); exit; } $vendorId = $vendor ? (int)$vendor['id'] : 1; // Default vendor for admin $error = ''; $success = ''; // ดึงหมวดหมู่สินค้าทั้งหมดจาก DB $categories = []; try { $catStmt = $db->query("SELECT * FROM categories ORDER BY name ASC"); if ($catStmt) $categories = $catStmt->fetchAll() ?: []; } catch (PDOException $e) {} // Fetch active brands $brands = []; try { $brandStmt = $db->query("SELECT * FROM brands WHERE is_active = 1 ORDER BY name ASC"); if ($brandStmt) $brands = $brandStmt->fetchAll() ?: []; } catch (PDOException $e) {} // Fetch active sizes $sizesList = []; try { $sizeStmt = $db->query("SELECT * FROM sizes WHERE is_active = 1 ORDER BY sort_order ASC, id ASC"); if ($sizeStmt) $sizesList = $sizeStmt->fetchAll() ?: []; } catch (PDOException $e) {} // ดึงหมวดหมู่เฉพาะของร้านค้า (ถ้ามี) $vendorCategories = []; if ($vendor) { try { $vcatStmt = $db->prepare("SELECT * FROM vendor_categories WHERE vendor_id = ? AND is_active = 1 ORDER BY name ASC"); $vcatStmt->execute([$vendorId]); $vendorCategories = $vcatStmt->fetchAll() ?: []; } catch (PDOException $e) {} } // ประมวลผลเมื่อส่งฟอร์ม (POST Request) if ($_SERVER['REQUEST_METHOD'] === 'POST') { $name = trim($_POST['name'] ?? ''); $categorySelect = trim($_POST['category_id'] ?? ''); $newCategoryName = trim($_POST['new_category_name'] ?? ''); $vcategoryId = (int)($_POST['vendor_category_id'] ?? 0); $brandId = (int)($_POST['brand_id'] ?? 0); $price = (float)($_POST['price'] ?? 0); $stock = (int)($_POST['stock'] ?? 0); $stdSizes = isset($_POST['sizes']) && is_array($_POST['sizes']) ? $_POST['sizes'] : []; $customSizesStr = $_POST['custom_sizes'] ?? ''; $customSizesArr = array_filter(array_map('trim', explode(',', $customSizesStr)), function($v) { return $v !== ''; }); $allSizesArr = array_unique(array_merge($stdSizes, $customSizesArr)); $sizes = implode(', ', $allSizesArr); $colors = trim($_POST['colors'] ?? ''); $description = trim($_POST['description'] ?? ''); // Validations if (empty($name)) { $error = "กรุณาระบุชื่อสินค้า"; } else if (empty($categorySelect)) { $error = "กรุณาเลือกหมวดหมู่สินค้า"; } else if ($categorySelect === 'new' && empty($newCategoryName)) { $error = "กรุณาระบุชื่อหมวดหมู่ใหม่ที่ต้องการเพิ่ม"; } else if ($price <= 0) { $error = "กรุณาระบุราคาสินค้าให้ถูกต้อง"; } else if ($stock < 0) { $error = "จำนวนสต็อกต้องไม่ติดลบ"; } else { // --- 1. จัดการหมวดหมู่สินค้า (Category Handling) --- $finalCategoryId = 0; if ($categorySelect === 'new') { // เช็คว่ามีหมวดหมู่นี้ในระบบแล้วหรือยัง (ป้องกันชื่อซ้ำ) $checkCatStmt = $db->prepare("SELECT id FROM categories WHERE LOWER(name) = LOWER(?) LIMIT 1"); $checkCatStmt->execute([$newCategoryName]); $existingCat = $checkCatStmt->fetch(); if ($existingCat) { // หากมีแล้ว ให้ดึง ID เดิมมาใช้ $finalCategoryId = (int)$existingCat['id']; } else { // หากยังไม่มี ให้บันทึกหมวดหมู่ใหม่ลง DB (พร้อมผูก vendor_id) // สร้าง slug สำหรับหมวดหมู่ใหม่ (รองรับภาษาไทย) $slug = preg_replace('/\s+/', '-', $newCategoryName); $slug = preg_replace('/[^\p{L}\p{N}\-]+/u', '', $slug); $slug = trim($slug, '-'); if (empty($slug)) { $slug = 'category'; } // ต่อท้ายเพื่อป้องกันชื่อหมวดหมู่ซ้ำ (เผื่อมีคนตั้งชื่อเหมือนกันเป๊ะแต่ระบบหาไม่เจอ) $slug = strtolower($slug) . '-' . time() . '-' . rand(100, 999); // เช็คว่าตาราง categories มีคอลัมน์ vendor_id หรือไม่ $colCheck = $db->query("SHOW COLUMNS FROM categories LIKE 'vendor_id'")->fetch(); if (!$colCheck) { $db->exec("ALTER TABLE categories ADD COLUMN vendor_id INT NULL AFTER id"); } $insertCatStmt = $db->prepare("INSERT INTO categories (name, slug, vendor_id, created_at) VALUES (?, ?, ?, NOW())"); $insertCatStmt->execute([$newCategoryName, $slug, $vendorId]); $finalCategoryId = (int)$db->lastInsertId(); } } else { $finalCategoryId = (int)$categorySelect; } // --- 2. จัดการอัปโหลดไฟล์รูปภาพสินค้า (Multiple Images) --- $uploadedImages = []; $allowedExtensions = ['jpg', 'jpeg', 'png', 'webp', 'gif']; $uploadDir = __DIR__ . '/uploads/products/'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } $filesToProcess = []; if (isset($_FILES['images']) && is_array($_FILES['images']['name'])) { $count = count($_FILES['images']['name']); for ($i = 0; $i < $count; $i++) { if ($_FILES['images']['error'][$i] === UPLOAD_ERR_OK) { $filesToProcess[] = [ 'tmp_name' => $_FILES['images']['tmp_name'][$i], 'name' => $_FILES['images']['name'][$i], 'size' => $_FILES['images']['size'][$i], 'error' => $_FILES['images']['error'][$i] ]; } elseif ($_FILES['images']['error'][$i] !== UPLOAD_ERR_NO_FILE) { $error = "พบข้อผิดพลาดจากเซิร์ฟเวอร์ขณะอัปโหลดไฟล์ที่ " . ($i + 1) . " (Error Code: " . $_FILES['images']['error'][$i] . ")"; break; } } } elseif (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $filesToProcess[] = [ 'tmp_name' => $_FILES['image']['tmp_name'], 'name' => $_FILES['image']['name'], 'size' => $_FILES['image']['size'], 'error' => $_FILES['image']['error'] ]; } if (empty($error) && empty($filesToProcess)) { $error = "กรุณาเลือกรูปภาพสินค้าอย่างน้อย 1 รูป"; } if (empty($error)) { require_once __DIR__ . '/../includes/image_helper.php'; foreach ($filesToProcess as $idx => $file) { $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($fileExtension, $allowedExtensions)) { $error = "ไฟล์รูปภาพ '" . htmlspecialchars($file['name']) . "' ไม่ได้รับอนุญาต (รองรับเฉพาะ JPG, PNG, WEBP, GIF)"; break; } if ($file['size'] > 10 * 1024 * 1024) { $error = "ไฟล์รูปภาพ '" . htmlspecialchars($file['name']) . "' มีขนาดใหญ่เกินไป (จำกัดไม่เกิน 10MB)"; break; } // เรียกใช้ฟังก์ชัน processAndUploadImage เพื่อบีบอัดและปรับขนาดรูป $uploadResult = processAndUploadImage($file, 'products'); if ($uploadResult['success']) { // processAndUploadImage จะคืนค่า main_path เป็น 'products/img_xxx.jpg' // เราจะนำมาต่อกับ 'uploads/' เพื่อเก็บลงฐานข้อมูลให้ตรงกับโครงสร้าง $uploadedImages[] = 'uploads/' . $uploadResult['main_path']; } else { $error = "เกิดข้อผิดพลาดในการบันทึกรูปภาพ '" . htmlspecialchars($file['name']) . "': " . $uploadResult['error']; break; } } } $imagePath = !empty($uploadedImages) ? $uploadedImages[0] : ''; // ถ้ามีข้อผิดพลาดเกี่ยวกับการอัปโหลด ให้หยุดและไม่ไปต่อ if (!empty($error)) { // ไม่ต้องทำอะไร ปล่อยให้ error ไปแสดงผลใน HTML } else { // --- 3. บันทึกสินค้าเข้าตาราง products --- if (empty($error) && $finalCategoryId > 0) { try { // เช็คและสร้างคอลัมน์ stock, sizes, colors ให้อัตโนมัติถ้ายังไม่มี $stockColCheck = $db->query("SHOW COLUMNS FROM products LIKE 'stock'")->fetch(); if (!$stockColCheck) { $db->exec("ALTER TABLE products ADD COLUMN stock INT NOT NULL DEFAULT 0 AFTER price"); } $sizesColCheck = $db->query("SHOW COLUMNS FROM products LIKE 'sizes'")->fetch(); if (!$sizesColCheck) { $db->exec("ALTER TABLE products ADD COLUMN sizes VARCHAR(255) NULL AFTER stock"); } $colorsColCheck = $db->query("SHOW COLUMNS FROM products LIKE 'colors'")->fetch(); if (!$colorsColCheck) { $db->exec("ALTER TABLE products ADD COLUMN colors VARCHAR(255) NULL AFTER sizes"); } // เช็คคอลัมน์ main_image และ image ในตาราง products $hasMainImage = $db->query("SHOW COLUMNS FROM products LIKE 'main_image'")->fetch(); $hasImage = $db->query("SHOW COLUMNS FROM products LIKE 'image'")->fetch(); if (!$hasMainImage && !$hasImage) { $db->exec("ALTER TABLE products ADD COLUMN main_image VARCHAR(255) NULL, ADD COLUMN image VARCHAR(255) NULL"); } elseif ($hasMainImage && !$hasImage) { $db->exec("ALTER TABLE products ADD COLUMN image VARCHAR(255) NULL"); } elseif (!$hasMainImage && $hasImage) { $db->exec("ALTER TABLE products ADD COLUMN main_image VARCHAR(255) NULL"); } // สร้าง slug สำหรับสินค้า (รองรับภาษาไทย) $productSlug = preg_replace('/\s+/', '-', $name); $productSlug = preg_replace('/[^\p{L}\p{N}\-]+/u', '', $productSlug); $productSlug = trim($productSlug, '-'); if (empty($productSlug)) { $productSlug = 'product'; } $productSlug = strtolower($productSlug) . '-' . time() . '-' . rand(1000, 9999); // บันทึกลงตาราง products $insertStmt = $db->prepare(" INSERT INTO products (vendor_id, category_id, vendor_category_id, brand_id, name, slug, price, stock, sizes, colors, description, main_image, image, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW()) "); $insertStmt->execute([ $vendorId, $finalCategoryId, $vcategoryId ?: null, $brandId ?: null, $name, $productSlug, $price, $stock, $sizes ?: null, $colors ?: null, $description, $imagePath, $imagePath ]); $productId = (int)$db->lastInsertId(); // บันทึกรูปภาพทั้งหมดลงตาราง product_images (Gallery) if (!empty($uploadedImages)) { $db->exec("CREATE TABLE IF NOT EXISTS `product_images` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `product_id` INT NOT NULL, `image_path` VARCHAR(255) NOT NULL, `thumb_path` VARCHAR(255) NOT NULL, `sort_order` INT DEFAULT 0, FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"); $imgStmt = $db->prepare("INSERT INTO product_images (product_id, image_path, thumb_path, sort_order) VALUES (?, ?, ?, ?)"); foreach ($uploadedImages as $order => $imgPath) { $imgStmt->execute([$productId, $imgPath, $imgPath, $order]); } } $_SESSION['flash_success'] = "🎉 เพิ่มสินค้า **'" . htmlspecialchars($name) . "'** เรียบร้อยแล้ว!"; if ($vendor) { header("Location: shop.php?slug=" . urlencode($vendor['store_slug'])); } else { header("Location: index.php"); } exit; } catch (PDOException $e) { $error = "เกิดข้อผิดพลาดในการบันทึกข้อมูล DB: " . $e->getMessage(); } } } } } $pageTitle = "เพิ่มสินค้าใหม่"; require_once __DIR__ . '/../includes/header.php'; ?> <div class="container py-4"> <div class="row justify-content-center"> <div class="col-md-9 col-lg-7"> <div class="card card-custom shadow-lg border-0 rounded-4 overflow-hidden"> <div class="card-header bg-primary text-white text-center py-4"> <h3 class="fw-bold mb-1">📦 เพิ่มสินค้าใหม่เข้าสู่ร้านค้า</h3> <p class="mb-0 small opacity-75">กรอกข้อมูลรายละเอียดสินค้าของคุณให้ครบถ้วนเพื่อเริ่มวางขาย</p> </div> <div class="card-body p-4"> <?php if (!empty($error)): ?> <div class="alert alert-danger border-0 shadow-sm rounded-3 mb-4"> ⚠️ <?php echo $error; ?> </div> <?php endif; ?> <form method="POST" action="add-product.php" enctype="multipart/form-data"> <!-- ชื่อสินค้า --> <div class="mb-3"> <label for="name" class="form-label fw-bold">📦 ชื่อสินค้า (Product Name) <span class="text-danger">*</span></label> <input type="text" class="form-control rounded-3" id="name" name="name" placeholder="เช่น เสื้อยืด Oversize สีดำ ลายกราฟิก" required> </div> <div class="row g-3 mb-3"> <!-- หมวดหมู่หลัก (Category Select + New Dynamic Input) --> <div class="col-md-6"> <label for="category_id" class="form-label fw-bold">🏷️ หมวดหมู่หลัก (Category) <span class="text-danger">*</span></label> <select class="form-select rounded-3 mb-2" id="category_id" name="category_id" onchange="toggleNewCategoryInput(this.value)" required> <option value="">-- เลือกหมวดหมู่ --</option> <?php foreach ($categories as $cat): ?> <option value="<?php echo $cat['id']; ?>"><?php echo htmlspecialchars($cat['name']); ?></option> <?php endforeach; ?> <option value="new" class="fw-bold text-primary">+ เพิ่มหมวดหมู่ใหม่...</option> </select> <!-- ช่องกรอกชื่อหมวดหมู่ใหม่ (ซ่อนไว้เป็นค่าเริ่มต้น) --> <div id="new_category_wrapper" class="d-none mt-2"> <input type="text" class="form-control rounded-3 border-primary" id="new_category_name" name="new_category_name" placeholder="✍️ พิมพ์ชื่อหมวดหมู่ใหม่ที่นี่..."> <small class="text-primary" style="font-size: 11px;">* หมวดหมู่นี้จะถูกสร้างและผูกกับร้านค้าของคุณทันที</small> </div> </div> <!-- หมวดหมู่ย่อยของร้านค้า (Optional) --> <?php if (!empty($vendorCategories)): ?> <div class="col-md-4"> <label for="vendor_category_id" class="form-label fw-bold">🏬 หมวดหมู่ในร้านค้า</label> <select class="form-select rounded-3" id="vendor_category_id" name="vendor_category_id"> <option value="">-- เลือกหมวดหมู่ในร้าน --</option> <?php foreach ($vendorCategories as $vcat): ?> <option value="<?php echo $vcat['id']; ?>"><?php echo htmlspecialchars($vcat['name']); ?></option> <?php endforeach; ?> </select> </div> <?php endif; ?> <!-- แบรนด์สินค้า (Brand) --> <div class="col-md-<?php echo !empty($vendorCategories) ? '4' : '6'; ?>"> <label for="brand_id" class="form-label fw-bold">🔖 แบรนด์สินค้า (Brand)</label> <select class="form-select rounded-3" id="brand_id" name="brand_id"> <option value="">-- ไม่ระบุแบรนด์ --</option> <?php foreach ($brands as $brand): ?> <option value="<?php echo $brand['id']; ?>"><?php echo htmlspecialchars($brand['name']); ?></option> <?php endforeach; ?> </select> </div> </div> <div class="row g-3 mb-3"> <!-- ราคาสินค้า --> <div class="col-md-6"> <label for="price" class="form-label fw-bold">💰 ราคาสินค้า (Price ฿) <span class="text-danger">*</span></label> <div class="input-group"> <span class="input-group-text bg-light">฿</span> <input type="number" step="0.01" min="0" class="form-control rounded-end-3" id="price" name="price" placeholder="290.00" required> </div> </div> <!-- จำนวนสต็อก --> <div class="col-md-6"> <label for="stock" class="form-label fw-bold">📊 จำนวนสต็อก (Stock) <span class="text-danger">*</span></label> <input type="number" min="0" class="form-control rounded-3" id="stock" name="stock" placeholder="50" required> </div> </div> <div class="row g-3 mb-3"> <!-- ไซส์ (Sizes) --> <div class="col-md-6"> <label class="form-label fw-bold">📏 ไซส์ (Sizes) <small class="text-muted fw-normal">(เลือกได้หลายข้อ)</small></label> <div class="d-flex flex-wrap gap-2 mt-1 mb-2"> <?php foreach ($sizesList as $sz): ?> <div class="form-check"> <input class="form-check-input" type="checkbox" name="sizes[]" value="<?php echo htmlspecialchars($sz['name']); ?>" id="size_<?php echo $sz['id']; ?>"> <label class="form-check-label" for="size_<?php echo $sz['id']; ?>"> <?php echo htmlspecialchars($sz['name']); ?> </label> </div> <?php endforeach; ?> </div> <input type="text" class="form-control rounded-3" id="custom_sizes" name="custom_sizes" placeholder="ไซส์อื่นๆ เช่น Free Size, 38, 40 (คั่นด้วย ,)"> </div> <!-- สี (Colors) --> <div class="col-md-6"> <label for="colors" class="form-label fw-bold">🎨 สี (Colors) <small class="text-muted fw-normal">(ถ้ามี)</small></label> <input type="text" class="form-control rounded-3" id="colors" name="colors" placeholder="เช่น ดำ, ขาว, แดง (คั่นด้วยจุลภาค ,)"> <div class="form-text text-muted small">ระบุหลายสีโดยคั่นด้วยเครื่องหมาย ,</div> </div> </div> <!-- รายละเอียดสินค้า --> <div class="mb-3"> <label for="description" class="form-label fw-bold">📝 รายละเอียดสินค้า (Description)</label> <textarea class="form-control rounded-3" id="description" name="description" rows="4" placeholder="ระบุขนาด, เนื้อผ้า, สี หรือข้อมูลสำคัญอื่นๆ ของสินค้า..."></textarea> </div> <!-- รูปภาพสินค้า (Multiple Image Upload) --> <div class="mb-4"> <label for="images" class="form-label fw-bold">📷 รูปภาพสินค้า (Product Images) <small class="text-muted fw-normal">(เลือกอัปโหลดหลายรูปได้)</small></label> <input type="file" class="form-control rounded-3" id="images" name="images[]" accept="image/*" multiple onchange="previewImages(event)"> <div class="form-text text-muted small">รองรับไฟล์ JPG, PNG, WEBP, GIF (กด Ctrl หรือ Shift เพื่อเลือกหลายรูปพร้อมกัน รูปแรกจะเป็นรูปหลักของสินค้า)</div> <!-- พื้นที่แสดงตัวอย่างรูปภาพ (Image Preview Container) --> <div id="imagePreviewContainer" class="row row-cols-3 row-cols-sm-4 row-cols-md-5 g-2 mt-2"></div> </div> <!-- ปุ่มดำเนินการ --> <div class="d-flex gap-2"> <a href="<?php echo $vendor ? 'shop.php?slug=' . urlencode($vendor['store_slug']) : 'index.php'; ?>" class="btn btn-outline-secondary rounded-pill px-4"> ยกเลิก </a> <button type="submit" class="btn btn-success flex-grow-1 py-2 rounded-pill fw-bold fs-6 shadow-sm"> 🚀 บันทึกและลงขายสินค้า (Publish Product) </button> </div> </form> </div> </div> </div> </div> </div> <script> function toggleNewCategoryInput(val) { const wrapper = document.getElementById('new_category_wrapper'); const input = document.getElementById('new_category_name'); if (val === 'new') { wrapper.classList.remove('d-none'); input.setAttribute('required', 'required'); input.focus(); } else { wrapper.classList.add('d-none'); input.removeAttribute('required'); input.value = ''; } } function previewImages(event) { const container = document.getElementById('imagePreviewContainer'); container.innerHTML = ''; const files = event.target.files; if (!files || files.length === 0) { return; } Array.from(files).forEach((file, index) => { if (!file.type.startsWith('image/')) return; const reader = new FileReader(); reader.onload = function(e) { const col = document.createElement('div'); col.className = 'col'; const isMain = index === 0; col.innerHTML = ` <div class="card h-100 border rounded-3 overflow-hidden shadow-sm position-relative"> <img src="${e.target.result}" class="card-img-top" style="height: 90px; object-fit: cover;" alt="Preview"> ${isMain ? '<span class="position-absolute top-0 start-0 bg-primary text-white badge m-1" style="font-size: 10px;">รูปหลัก</span>' : ''} <div class="card-footer p-1 bg-light text-center"> <small class="text-truncate d-block text-muted" style="font-size: 10px; max-width: 100%;">${file.name}</small> </div> </div> `; container.appendChild(col); }; reader.readAsDataURL(file); }); } </script> <?php require_once __DIR__ . '/../includes/footer.php'; ?>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.26 |
proxy
|
phpinfo
|
Settings