350 lines
9.8 KiB
PHP
350 lines
9.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
function init_store(): void {
|
|
ensure_schema();
|
|
seed_data();
|
|
}
|
|
|
|
function ensure_schema(): void {
|
|
$pdo = db();
|
|
$pdo->exec(
|
|
'CREATE TABLE IF NOT EXISTS categories (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(120) NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
|
);
|
|
|
|
$pdo->exec(
|
|
'CREATE TABLE IF NOT EXISTS products (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
name VARCHAR(160) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
price DECIMAL(10,2) NOT NULL,
|
|
stock INT NOT NULL DEFAULT 0,
|
|
rating DECIMAL(3,2) NOT NULL DEFAULT 0.00,
|
|
category_id INT NULL,
|
|
image_url VARCHAR(255) NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX (category_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
|
);
|
|
|
|
$pdo->exec(
|
|
'CREATE TABLE IF NOT EXISTS orders (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
order_number VARCHAR(20) NOT NULL,
|
|
customer_name VARCHAR(120) NOT NULL,
|
|
customer_email VARCHAR(160) NOT NULL,
|
|
customer_address VARCHAR(255) NOT NULL,
|
|
status VARCHAR(30) NOT NULL,
|
|
total_price DECIMAL(10,2) NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE KEY (order_number)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
|
);
|
|
|
|
$pdo->exec(
|
|
'CREATE TABLE IF NOT EXISTS order_items (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
order_id INT NOT NULL,
|
|
product_id INT NOT NULL,
|
|
quantity INT NOT NULL,
|
|
price DECIMAL(10,2) NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX (order_id),
|
|
INDEX (product_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
|
);
|
|
}
|
|
|
|
function seed_data(): void {
|
|
$pdo = db();
|
|
$count = (int) $pdo->query('SELECT COUNT(*) FROM products')->fetchColumn();
|
|
if ($count > 0) {
|
|
return;
|
|
}
|
|
|
|
$categories = [
|
|
'Apparel',
|
|
'Electronics',
|
|
'Moroccan Goods'
|
|
];
|
|
|
|
$categoryStmt = $pdo->prepare('INSERT INTO categories (name) VALUES (:name)');
|
|
$categoryIds = [];
|
|
foreach ($categories as $name) {
|
|
$categoryStmt->execute([':name' => $name]);
|
|
$categoryIds[$name] = (int) $pdo->lastInsertId();
|
|
}
|
|
|
|
$products = [
|
|
[
|
|
'name' => 'Atlas Hoodie',
|
|
'description' => 'Midweight cotton hoodie with brushed interior and clean stitch detailing.',
|
|
'price' => 79.00,
|
|
'stock' => 24,
|
|
'rating' => 4.6,
|
|
'category' => 'Apparel'
|
|
],
|
|
[
|
|
'name' => 'Casablanca Sneakers',
|
|
'description' => 'Everyday sneaker with cushioned sole and minimalist profile.',
|
|
'price' => 98.00,
|
|
'stock' => 18,
|
|
'rating' => 4.4,
|
|
'category' => 'Apparel'
|
|
],
|
|
[
|
|
'name' => 'Rabat Smartwatch',
|
|
'description' => 'Slim fitness watch with sleep tracking and a 7-day battery.',
|
|
'price' => 149.00,
|
|
'stock' => 12,
|
|
'rating' => 4.2,
|
|
'category' => 'Electronics'
|
|
],
|
|
[
|
|
'name' => 'Sahara Bluetooth Speaker',
|
|
'description' => 'Portable speaker with clean bass and long-lasting battery.',
|
|
'price' => 129.00,
|
|
'stock' => 10,
|
|
'rating' => 4.3,
|
|
'category' => 'Electronics'
|
|
],
|
|
[
|
|
'name' => 'Fez Copper Lamp',
|
|
'description' => 'Hand-finished lamp with soft perforated light pattern.',
|
|
'price' => 119.00,
|
|
'stock' => 7,
|
|
'rating' => 4.8,
|
|
'category' => 'Moroccan Goods'
|
|
],
|
|
[
|
|
'name' => 'Agadir Ceramic Tagine',
|
|
'description' => 'Glazed ceramic tagine for slow cooking and elegant serving.',
|
|
'price' => 84.00,
|
|
'stock' => 9,
|
|
'rating' => 4.7,
|
|
'category' => 'Moroccan Goods'
|
|
]
|
|
];
|
|
|
|
$productStmt = $pdo->prepare(
|
|
'INSERT INTO products (name, description, price, stock, rating, category_id) VALUES (:name, :description, :price, :stock, :rating, :category_id)'
|
|
);
|
|
|
|
foreach ($products as $product) {
|
|
$productStmt->execute([
|
|
':name' => $product['name'],
|
|
':description' => $product['description'],
|
|
':price' => $product['price'],
|
|
':stock' => $product['stock'],
|
|
':rating' => $product['rating'],
|
|
':category_id' => $categoryIds[$product['category']] ?? null
|
|
]);
|
|
}
|
|
}
|
|
|
|
function get_categories(): array {
|
|
$stmt = db()->query('SELECT id, name FROM categories ORDER BY name ASC');
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_products(?string $search = null, ?int $categoryId = null, ?string $sort = null): array {
|
|
$sql = 'SELECT p.*, c.name AS category_name FROM products p LEFT JOIN categories c ON c.id = p.category_id WHERE 1=1';
|
|
$params = [];
|
|
|
|
if ($search) {
|
|
$sql .= ' AND (p.name LIKE :search OR p.description LIKE :search)';
|
|
$params[':search'] = '%' . $search . '%';
|
|
}
|
|
|
|
if ($categoryId) {
|
|
$sql .= ' AND p.category_id = :category_id';
|
|
$params[':category_id'] = $categoryId;
|
|
}
|
|
|
|
switch ($sort) {
|
|
case 'price_asc':
|
|
$sql .= ' ORDER BY p.price ASC';
|
|
break;
|
|
case 'price_desc':
|
|
$sql .= ' ORDER BY p.price DESC';
|
|
break;
|
|
default:
|
|
$sql .= ' ORDER BY p.created_at DESC';
|
|
break;
|
|
}
|
|
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_product(int $id): ?array {
|
|
$stmt = db()->prepare('SELECT p.*, c.name AS category_name FROM products p LEFT JOIN categories c ON c.id = p.category_id WHERE p.id = :id');
|
|
$stmt->execute([':id' => $id]);
|
|
$product = $stmt->fetch();
|
|
return $product ?: null;
|
|
}
|
|
|
|
function format_price(float $price): string {
|
|
return '$' . number_format($price, 2);
|
|
}
|
|
|
|
function product_image_data(string $label): string {
|
|
$clean = preg_replace('/[^A-Za-z0-9]/', '', $label);
|
|
$text = strtoupper(substr($clean, 0, 2));
|
|
if ($text === '') {
|
|
$text = 'ES';
|
|
}
|
|
$svg = "<svg xmlns='http://www.w3.org/2000/svg' width='600' height='400' viewBox='0 0 600 400'><rect width='600' height='400' fill='#f1f3f6'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' font-family='Inter, Arial, sans-serif' font-size='96' fill='#111827'>" . $text . "</text></svg>";
|
|
return 'data:image/svg+xml;utf8,' . rawurlencode($svg);
|
|
}
|
|
|
|
function cart_count(): int {
|
|
$cart = $_SESSION['cart'] ?? [];
|
|
return array_sum($cart);
|
|
}
|
|
|
|
function cart_items(): array {
|
|
$cart = $_SESSION['cart'] ?? [];
|
|
if (!$cart) {
|
|
return [];
|
|
}
|
|
|
|
$ids = array_keys($cart);
|
|
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
|
$stmt = db()->prepare(
|
|
"SELECT p.*, c.name AS category_name FROM products p LEFT JOIN categories c ON c.id = p.category_id WHERE p.id IN ($placeholders)"
|
|
);
|
|
$stmt->execute($ids);
|
|
$products = $stmt->fetchAll();
|
|
$indexed = [];
|
|
foreach ($products as $product) {
|
|
$indexed[(int) $product['id']] = $product;
|
|
}
|
|
|
|
$items = [];
|
|
foreach ($cart as $productId => $qty) {
|
|
$product = $indexed[(int) $productId] ?? null;
|
|
if (!$product) {
|
|
continue;
|
|
}
|
|
$lineTotal = ((float) $product['price']) * $qty;
|
|
$items[] = [
|
|
'product' => $product,
|
|
'quantity' => $qty,
|
|
'line_total' => $lineTotal
|
|
];
|
|
}
|
|
return $items;
|
|
}
|
|
|
|
function cart_total(): float {
|
|
$total = 0.0;
|
|
foreach (cart_items() as $item) {
|
|
$total += $item['line_total'];
|
|
}
|
|
return $total;
|
|
}
|
|
|
|
function cart_add(int $productId, int $quantity): void {
|
|
if ($quantity < 1) {
|
|
return;
|
|
}
|
|
$cart = $_SESSION['cart'] ?? [];
|
|
$cart[$productId] = ($cart[$productId] ?? 0) + $quantity;
|
|
$_SESSION['cart'] = $cart;
|
|
}
|
|
|
|
function cart_update(int $productId, int $quantity): void {
|
|
$cart = $_SESSION['cart'] ?? [];
|
|
if ($quantity < 1) {
|
|
unset($cart[$productId]);
|
|
} else {
|
|
$cart[$productId] = $quantity;
|
|
}
|
|
$_SESSION['cart'] = $cart;
|
|
}
|
|
|
|
function cart_clear(): void {
|
|
unset($_SESSION['cart']);
|
|
}
|
|
|
|
function create_order(array $customer, array $items): ?string {
|
|
if (!$items) {
|
|
return null;
|
|
}
|
|
|
|
$pdo = db();
|
|
$pdo->beginTransaction();
|
|
|
|
try {
|
|
$orderNumber = 'ES9-' . strtoupper(bin2hex(random_bytes(3)));
|
|
$total = 0.0;
|
|
foreach ($items as $item) {
|
|
$total += $item['line_total'];
|
|
}
|
|
|
|
$orderStmt = $pdo->prepare(
|
|
'INSERT INTO orders (order_number, customer_name, customer_email, customer_address, status, total_price) VALUES (:order_number, :customer_name, :customer_email, :customer_address, :status, :total_price)'
|
|
);
|
|
$orderStmt->execute([
|
|
':order_number' => $orderNumber,
|
|
':customer_name' => $customer['name'],
|
|
':customer_email' => $customer['email'],
|
|
':customer_address' => $customer['address'],
|
|
':status' => 'Processing',
|
|
':total_price' => $total
|
|
]);
|
|
|
|
$orderId = (int) $pdo->lastInsertId();
|
|
|
|
$itemStmt = $pdo->prepare(
|
|
'INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (:order_id, :product_id, :quantity, :price)'
|
|
);
|
|
|
|
foreach ($items as $item) {
|
|
$itemStmt->execute([
|
|
':order_id' => $orderId,
|
|
':product_id' => $item['product']['id'],
|
|
':quantity' => $item['quantity'],
|
|
':price' => $item['product']['price']
|
|
]);
|
|
}
|
|
|
|
$pdo->commit();
|
|
return $orderNumber;
|
|
} catch (Throwable $e) {
|
|
$pdo->rollBack();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function get_order_by_number(string $orderNumber): ?array {
|
|
$stmt = db()->prepare('SELECT * FROM orders WHERE order_number = :order_number');
|
|
$stmt->execute([':order_number' => $orderNumber]);
|
|
$order = $stmt->fetch();
|
|
return $order ?: null;
|
|
}
|
|
|
|
function get_order_items(int $orderId): array {
|
|
$stmt = db()->prepare(
|
|
'SELECT oi.*, p.name, p.description FROM order_items oi LEFT JOIN products p ON p.id = oi.product_id WHERE oi.order_id = :order_id'
|
|
);
|
|
$stmt->execute([':order_id' => $orderId]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function get_orders(): array {
|
|
$stmt = db()->query('SELECT * FROM orders ORDER BY created_at DESC');
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
function update_order_status(int $orderId, string $status): void {
|
|
$stmt = db()->prepare('UPDATE orders SET status = :status WHERE id = :id');
|
|
$stmt->execute([':status' => $status, ':id' => $orderId]);
|
|
}
|