use App\Helpers\Database;
use App\Helpers\Security;
use App\Helpers\Auth;
use PDO;
// ========================================================
// 1. SAVE (Creates & Updates with Full Schema & Image Upload)
// ========================================================
public function save(): void
{
$this->validateCsrf();
$id = !empty($_POST['vendor_id']) ? (int)$_POST['vendor_id'] : (!empty($_POST['id']) ? (int)$_POST['id'] : null);
$businessName = trim($_POST['business_name'] ?? '');
$ownerName = trim($_POST['owner_name'] ?? '');
$category = trim($_POST['category'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$city = trim($_POST['city'] ?? '');
$address = trim($_POST['address'] ?? '');
$servicesList = trim($_POST['services_list'] ?? '');
$price = !empty($_POST['price_starts_at']) ? max(0, (float)$_POST['price_starts_at']) : 0.00;
$isVerified = !empty($_POST['is_verified']) ? 1 : 0;
$status = in_array($_POST['status'] ?? 'active', ['active', 'inactive', 'suspended'], true) ? $_POST['status'] : 'active';
// Whitelist Subscription Plan
$allowedPlans = ['free', 'silver', 'gold', 'platinum'];
$plan = in_array(strtolower(trim($_POST['subscription_plan'] ?? 'free')), $allowedPlans, true) ? strtolower(trim($_POST['subscription_plan'])) : 'free';
// Safe Expiry Date check (Valid Y-m-d or NULL)
$rawDate = trim($_POST['subscription_expires_at'] ?? '');
$expiresAt = (!empty($rawDate) && strtotime($rawDate) !== false) ? date('Y-m-d', strtotime($rawDate)) : null;
// Mandatory fields check
if (empty($businessName) || empty($phone) || empty($city)) {
header('Location: /admin/vendors?error=missing_fields');
exit;
}
// Email format validation (agar provided ho)
if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$email = null;
}
// Secure File Upload Handler
$imagePath = null;
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$allowedMimes = ['image/jpeg', 'image/png', 'image/webp'];
$fileTmp = $_FILES['image']['tmp_name'];
$fileSize = $_FILES['image']['size'];
if ($fileSize <= 3 * 1024 * 1024) { // 3MB limit
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $fileTmp);
finfo_close($finfo);
if (in_array($mime, $allowedMimes, true)) {
$extMap = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
$ext = $extMap[$mime];
$baseDir = dirname(__DIR__, 2);
$uploadDir = is_dir($baseDir . '/public') ? $baseDir . '/public/uploads/vendors/' : $baseDir . '/uploads/vendors/';
// Hostinger Safe Directory Permission (0755)
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filename = 'vendor_' . time() . '_' . bin2hex(random_bytes(3)) . '.' . $ext;
if (move_uploaded_file($fileTmp, $uploadDir . $filename)) {
$imagePath = '/uploads/vendors/' . $filename;
}
}
}
}
try {
$db = Database::getConnection();
if (!empty($id)) {
// ================= UPDATE EXISTING VENDOR =================
if ($imagePath) {
// Purani image ko fetch karke naye image se replace karein
$old = $db->prepare("SELECT image FROM vendors WHERE id = ?");
$old->execute([$id]);
$oldImg = $old->fetchColumn();
if ($oldImg) {
$this->deleteVendorImage($oldImg);
}
$sql = "UPDATE vendors SET
business_name = ?, owner_name = ?, category = ?, phone = ?,
email = ?, city = ?, address = ?, services_list = ?,
price_starts_at = ?, subscription_plan = ?, subscription_expires_at = ?,
is_verified = ?, status = ?, image = ?
WHERE id = ?";
$params = [$businessName, $ownerName, $category, $phone, $email, $city, $address, $servicesList, $price, $plan, $expiresAt, $isVerified, $status, $imagePath, $id];
} else {
$sql = "UPDATE vendors SET
business_name = ?, owner_name = ?, category = ?, phone = ?,
email = ?, city = ?, address = ?, services_list = ?,
price_starts_at = ?, subscription_plan = ?, subscription_expires_at = ?,
is_verified = ?, status = ?
WHERE id = ?";
$params = [$businessName, $ownerName, $category, $phone, $email, $city, $address, $servicesList, $price, $plan, $expiresAt, $isVerified, $status, $id];
}
$db->prepare($sql)->execute($params);
if (class_exists('\App\Helpers\Security') && class_exists('\App\Helpers\Auth')) {
Security::logAudit(Auth::id(), 'UPDATE_VENDOR', 'vendors', $id);
}
} else {
// ================= INSERT NEW VENDOR =================
$sql = "INSERT INTO vendors
(business_name, owner_name, category, phone, email, city, address, services_list, price_starts_at, subscription_plan, subscription_expires_at, is_verified, status, image, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
$db->prepare($sql)->execute([$businessName, $ownerName, $category, $phone, $email, $city, $address, $servicesList, $price, $plan, $expiresAt, $isVerified, $status, $imagePath]);
$newId = (int)$db->lastInsertId();
if (class_exists('\App\Helpers\Security') && class_exists('\App\Helpers\Auth')) {
Security::logAudit(Auth::id(), 'CREATE_VENDOR', 'vendors', $newId);
}
}
header('Location: /admin/vendors?success=saved');
exit;
} catch (\Throwable $e) {
error_log('[VENDOR_SAVE_ERROR] ' . $e->getMessage());
header('Location: /admin/vendors?error=db_error');
exit;
}
}
// ========================================================
// 2. DELETE (Removes Vendor & Unlinks Image Storage)
// ========================================================
public function delete(): void
{
$this->validateCsrf();
$id = (int)($_POST['vendor_id'] ?? ($_POST['id'] ?? ($_GET['id'] ?? 0)));
if ($id <= 0) {
header('Location: /admin/vendors?error=invalid_id');
exit;
}
try {
$db = Database::getConnection();
// Fetch image path before deletion
$old = $db->prepare("SELECT image FROM vendors WHERE id = ?");
$old->execute([$id]);
$oldImg = $old->fetchColumn();
// Delete database row
$stmt = $db->prepare("DELETE FROM vendors WHERE id = ?");
$stmt->execute([$id]);
// Database delete success hone par hi photo unlink karein
if ($stmt->rowCount() > 0 && $oldImg) {
$this->deleteVendorImage($oldImg);
}
if (class_exists('\App\Helpers\Security') && class_exists('\App\Helpers\Auth')) {
Security::logAudit(Auth::id(), 'DELETE_VENDOR', 'vendors', $id);
}
header('Location: /admin/vendors?success=deleted');
exit;
} catch (\Throwable $e) {
error_log('[VENDOR_DELETE_ERROR] ' . $e->getMessage());
header('Location: /admin/vendors?error=db_error');
exit;
}
}
// ========================================================
// 3. TOGGLE VERIFY (One-Click Verification Inversion)
// ========================================================
public function toggleVerify(): void
{
$this->validateCsrf();
$id = (int)($_POST['vendor_id'] ?? ($_POST['id'] ?? ($_GET['id'] ?? 0)));
if ($id <= 0) {
header('Location: /admin/vendors?error=invalid_id');
exit;
}
try {
$db = Database::getConnection();
$stmt = $db->prepare("UPDATE vendors SET is_verified = 1 - is_verified WHERE id = ?");
$stmt->execute([$id]);
if (class_exists('\App\Helpers\Security') && class_exists('\App\Helpers\Auth')) {
Security::logAudit(Auth::id(), 'TOGGLE_VENDOR_VERIFY', 'vendors', $id);
}
header('Location: /admin/vendors?success=status_updated');
exit;
} catch (\Throwable $e) {
error_log('[VENDOR_TOGGLE_ERROR] ' . $e->getMessage());
header('Location: /admin/vendors?error=db_error');
exit;
}
}
// ========================================================
// PRIVATE HELPER: SAFE IMAGE UNLINKER
// ========================================================
private function deleteVendorImage(?string $imagePath): void
{
if (empty($imagePath)) return;
$base = dirname(__DIR__, 2);
$paths = [
$base . '/public' . $imagePath,
$base . $imagePath
];
foreach ($paths as $file) {
if (file_exists($file) && is_file($file)) {
@unlink($file);
break;
}
}
}
Warning: http_response_code(): Cannot set response code - headers already sent (output started at /home/u163000859/domains/dsa.midexa.in/public_html/app/Controllers/VendorController.php:1) in /home/u163000859/domains/dsa.midexa.in/public_html/app/Helpers/Router.php on line 121