PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); self::$instance->exec('PRAGMA foreign_keys = ON;'); } else { $host = Config::get('DB_HOST', '127.0.0.1'); $port = Config::get('DB_PORT', '3306'); $database = Config::get('DB_DATABASE', 'order_system'); $username = Config::get('DB_USERNAME', 'root'); $password = Config::get('DB_PASSWORD', ''); $dsn = "mysql:host={$host};port={$port};dbname={$database};charset=utf8mb4"; self::$instance = new PDO($dsn, $username, $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); } } catch (PDOException $e) { die('Database Connection Error: ' . $e->getMessage()); } } return self::$instance; } /** * Initializes database tables and seed data for the Collaborative Canteen Ordering System */ public static function initSchema(): void { $db = self::getConnection(); $isSqlite = Config::get('DB_CONNECTION', 'sqlite') === 'sqlite'; // Auto-migration check: If old schema exists without user_id / creator_id, drop old tables if ($isSqlite) { $checkTable = $db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='employee_orders'")->fetchColumn(); if ($checkTable) { $columns = $db->query("PRAGMA table_info(employee_orders)")->fetchAll(); $hasUserId = false; foreach ($columns as $col) { if ($col['name'] === 'user_id') { $hasUserId = true; break; } } if (!$hasUserId) { $db->exec("DROP TABLE IF EXISTS employee_order_items;"); $db->exec("DROP TABLE IF EXISTS employee_orders;"); $db->exec("DROP TABLE IF EXISTS session_menus;"); $db->exec("DROP TABLE IF EXISTS daily_sessions;"); $db->exec("DROP TABLE IF EXISTS employees;"); $db->exec("DROP TABLE IF EXISTS users;"); $db->exec("DROP TABLE IF EXISTS orders;"); $db->exec("DROP TABLE IF EXISTS order_items;"); $db->exec("DROP TABLE IF EXISTS products;"); $db->exec("DROP TABLE IF EXISTS customers;"); } } } $autoIncrement = $isSqlite ? 'AUTOINCREMENT' : 'AUTO_INCREMENT'; $dateTimeDefault = $isSqlite ? "DATETIME DEFAULT (datetime('now', 'localtime'))" : 'DATETIME DEFAULT CURRENT_TIMESTAMP'; // Users Table $db->exec(" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY {$autoIncrement}, name VARCHAR(255) NOT NULL, username VARCHAR(100) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at {$dateTimeDefault} ); "); // Daily Sessions Table $db->exec(" CREATE TABLE IF NOT EXISTS daily_sessions ( id INTEGER PRIMARY KEY {$autoIncrement}, creator_id INTEGER NOT NULL, session_date DATE NOT NULL, title VARCHAR(255) NOT NULL, canteen_name VARCHAR(255) NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'open', notes TEXT DEFAULT NULL, created_at {$dateTimeDefault}, updated_at {$dateTimeDefault}, FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE ); "); // Session Menus Table $db->exec(" CREATE TABLE IF NOT EXISTS session_menus ( id INTEGER PRIMARY KEY {$autoIncrement}, session_id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, final_price DECIMAL(10,2) DEFAULT 0.00, created_at {$dateTimeDefault}, FOREIGN KEY (session_id) REFERENCES daily_sessions(id) ON DELETE CASCADE ); "); // Employee Orders Table $db->exec(" CREATE TABLE IF NOT EXISTS employee_orders ( id INTEGER PRIMARY KEY {$autoIncrement}, session_id INTEGER NOT NULL, user_id INTEGER NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'unpaid', notes TEXT DEFAULT NULL, total_bill DECIMAL(10,2) NOT NULL DEFAULT 0.00, created_at {$dateTimeDefault}, updated_at {$dateTimeDefault}, FOREIGN KEY (session_id) REFERENCES daily_sessions(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); "); // Employee Order Items Table $db->exec(" CREATE TABLE IF NOT EXISTS employee_order_items ( id INTEGER PRIMARY KEY {$autoIncrement}, employee_order_id INTEGER NOT NULL, session_menu_id INTEGER DEFAULT NULL, item_name VARCHAR(255) NOT NULL, quantity INTEGER NOT NULL DEFAULT 1, unit_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, total_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, notes VARCHAR(255) DEFAULT NULL, created_at {$dateTimeDefault}, FOREIGN KEY (employee_order_id) REFERENCES employee_orders(id) ON DELETE CASCADE ); "); // Check if seed data exists $stmt = $db->query("SELECT COUNT(*) FROM users"); $count = (int)$stmt->fetchColumn(); if ($count === 0) { self::seedInitialData($db); } } private static function seedInitialData(PDO $db): void { $hash = password_hash('password', PASSWORD_DEFAULT); // Seed Users $users = [ ['Dea (Coordinator)', 'dea', 'dea@company.com', $hash], ['Alex Turner', 'alex', 'alex@company.com', $hash], ['Sarah Jenkins', 'sarah', 'sarah@company.com', $hash], ['Budi Santoso', 'budi', 'budi@company.com', $hash], ['Michael Chen', 'michael', 'michael@company.com', $hash], ]; $stmtUser = $db->prepare("INSERT INTO users (name, username, email, password_hash) VALUES (?, ?, ?, ?)"); foreach ($users as $u) { $stmtUser->execute($u); } // Seed Today's Session created by Dea (id = 1) $today = date('Y-m-d'); $title = "Dea's Lunch Hub - Kantin Bu Siti"; $stmtSession = $db->prepare("INSERT INTO daily_sessions (creator_id, session_date, title, canteen_name, status, notes) VALUES (1, ?, ?, 'Kantin Bu Siti', 'open', 'Please submit your lunch orders before 11:30 AM!')"); $stmtSession->execute([$today, $title]); $sessionId = (int)$db->lastInsertId(); // Seed Menu Items $menuItems = [ 'Ayam Bakar Madu + Nasi', 'Ayam Geprek Sambal Bawang + Nasi', 'Nasi Goreng Spesial', 'Mie Goreng Seafood', 'Soto Ayam Lamongan + Nasi', 'Gado-Gado Lontong', 'Tahu & Tempe Goreng (Extra)', 'Es Teh Manis', 'Es Jeruk Segar', ]; $stmtMenu = $db->prepare("INSERT INTO session_menus (session_id, name, final_price) VALUES (?, ?, 0.00)"); foreach ($menuItems as $item) { $stmtMenu->execute([$sessionId, $item]); } // Seed an order for Alex (user_id = 2) $stmtOrder = $db->prepare("INSERT INTO employee_orders (session_id, user_id, status, notes, total_bill) VALUES (?, 2, 'unpaid', 'Pedas sedang ya', 0.00)"); $stmtOrder->execute([$sessionId]); $alexOrderId = (int)$db->lastInsertId(); $stmtItem = $db->prepare("INSERT INTO employee_order_items (employee_order_id, session_menu_id, item_name, quantity, unit_price, total_price, notes) VALUES (?, ?, ?, ?, 0.00, 0.00, ?)"); $stmtItem->execute([$alexOrderId, 1, 'Ayam Bakar Madu + Nasi', 1, 'Pedas sedang']); $stmtItem->execute([$alexOrderId, 8, 'Es Teh Manis', 1, 'Kurang manis / less sugar']); } }