Files
canteenhub/app/Core/Database/Database.php
T

229 lines
9.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Core\Database;
use App\Core\Config;
use PDO;
use PDOException;
class Database
{
private static ?PDO $instance = null;
public static function getConnection(): PDO
{
if (self::$instance === null) {
$connection = Config::get('DB_CONNECTION', 'sqlite');
try {
if ($connection === 'sqlite') {
$databasePath = Config::get('DB_DATABASE', __DIR__ . '/../../../storage/database.sqlite');
$dir = dirname($databasePath);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
self::$instance = new PDO("sqlite:{$databasePath}", null, null, [
PDO::ATTR_ERRMODE => 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']);
}
}