initDatabase(); $this->handleCors(); $this->validateApiKey(); } /** * Datenbank-Verbindung initialisieren */ private function initDatabase() { $connectionParams = [ 'dbname' => getenv('DB_DATABASE') ?: 'freeshop', 'user' => getenv('DB_USERNAME') ?: 'freeshop_user', 'password' => getenv('DB_PASSWORD') ?: 'freeshop_password', 'host' => getenv('DB_HOST') ?: 'db', 'driver' => 'pdo_mysql', 'port' => getenv('DB_PORT') ?: 3306, 'charset' => 'utf8mb4', ]; try { $this->conn = DriverManager::getConnection($connectionParams); } catch (Exception $e) { $this->sendError('Database connection failed', 500); } } /** * CORS-Header setzen */ private function handleCors() { $origin = $_SERVER['HTTP_ORIGIN'] ?? '*'; if (in_array('*', $this->allowedOrigins) || in_array($origin, $this->allowedOrigins)) { header('Access-Control-Allow-Origin: ' . $origin); } header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization, X-API-Key'); header('Access-Control-Max-Age: 86400'); if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } } /** * API-Key validieren */ private function validateApiKey() { $apiKey = $_SERVER['HTTP_X_API_KEY'] ?? $_GET['api_key'] ?? null; if (!$apiKey) { $this->sendError('API key required', 401); } // API-Key in der Datenbank validieren try { $stmt = $this->conn->prepare('SELECT * FROM ws_api_key WHERE key_value = ? AND active = 1'); $stmt->execute([$apiKey]); $keyData = $stmt->fetchAssociative(); if (!$keyData) { $this->sendError('Invalid API key', 401); } $this->apiKey = $keyData; } catch (Exception $e) { $this->sendError('API key validation failed', 500); } } /** * Produkte abrufen */ public function getProducts() { $page = max(1, intval($_GET['page'] ?? 1)); $limit = min(50, max(1, intval($_GET['limit'] ?? 20))); $category = $_GET['category'] ?? null; $search = $_GET['search'] ?? null; $minPrice = $_GET['min_price'] ?? null; $maxPrice = $_GET['max_price'] ?? null; $offset = ($page - 1) * $limit; try { $whereConditions = ['p.active = 1']; $params = []; if ($category) { $whereConditions[] = 'p.category_id = ?'; $params[] = $category; } if ($search) { $whereConditions[] = '(p.name LIKE ? OR p.description LIKE ?)'; $params[] = '%' . $search . '%'; $params[] = '%' . $search . '%'; } if ($minPrice) { $whereConditions[] = 'p.price >= ?'; $params[] = $minPrice; } if ($maxPrice) { $whereConditions[] = 'p.price <= ?'; $params[] = $maxPrice; } $whereClause = implode(' AND ', $whereConditions); // Gesamtanzahl $countSql = " SELECT COUNT(*) as total FROM ws_product p LEFT JOIN ws_category c ON p.category_id = c.id WHERE $whereClause "; $stmt = $this->conn->prepare($countSql); $stmt->execute($params); $totalCount = $stmt->fetchAssociative()['total']; // Produkte laden $sql = " SELECT p.*, c.name as category_name FROM ws_product p LEFT JOIN ws_category c ON p.category_id = c.id WHERE $whereClause ORDER BY p.created_at DESC LIMIT $limit OFFSET $offset "; $stmt = $this->conn->prepare($sql); $stmt->execute($params); $products = $stmt->fetchAllAssociative(); $this->sendResponse([ 'success' => true, 'data' => $products, 'pagination' => [ 'page' => $page, 'limit' => $limit, 'total' => $totalCount, 'pages' => ceil($totalCount / $limit) ] ]); } catch (Exception $e) { $this->sendError('Failed to fetch products: ' . $e->getMessage(), 500); } } /** * Einzelnes Produkt abrufen */ public function getProduct($id) { try { $stmt = $this->conn->prepare(' SELECT p.*, c.name as category_name FROM ws_product p LEFT JOIN ws_category c ON p.category_id = c.id WHERE p.id = ? AND p.active = 1 '); $stmt->execute([$id]); $product = $stmt->fetchAssociative(); if (!$product) { $this->sendError('Product not found', 404); } // Bewertungen laden $stmt = $this->conn->prepare(' SELECT r.*, c.first_name, c.last_name FROM ws_review r LEFT JOIN ws_customer c ON r.customer_id = c.id WHERE r.product_id = ? AND r.active = 1 ORDER BY r.created_at DESC '); $stmt->execute([$id]); $reviews = $stmt->fetchAllAssociative(); $product['reviews'] = $reviews; $this->sendResponse([ 'success' => true, 'data' => $product ]); } catch (Exception $e) { $this->sendError('Failed to fetch product: ' . $e->getMessage(), 500); } } /** * Kategorien abrufen */ public function getCategories() { try { $stmt = $this->conn->prepare(' SELECT * FROM ws_category WHERE active = 1 ORDER BY sort_order ASC, name ASC '); $stmt->execute(); $categories = $stmt->fetchAllAssociative(); $this->sendResponse([ 'success' => true, 'data' => $categories ]); } catch (Exception $e) { $this->sendError('Failed to fetch categories: ' . $e->getMessage(), 500); } } /** * Bestellung erstellen */ public function createOrder() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } $required = ['customer_name', 'customer_email', 'items']; foreach ($required as $field) { if (empty($input[$field])) { $this->sendError("Missing required field: $field", 400); } } try { $this->conn->beginTransaction(); // Bestellung erstellen $stmt = $this->conn->prepare(' INSERT INTO ws_order (customer_name, customer_email, customer_phone, total_amount, status, created_at) VALUES (?, ?, ?, ?, ?, NOW()) '); $totalAmount = 0; foreach ($input['items'] as $item) { $totalAmount += $item['price'] * $item['quantity']; } $stmt->execute([ $input['customer_name'], $input['customer_email'], $input['customer_phone'] ?? '', $totalAmount, 'pending' ]); $orderId = $this->conn->lastInsertId(); // Bestellpositionen erstellen $stmt = $this->conn->prepare(' INSERT INTO ws_order_item (order_id, product_id, product_name, quantity, price, total_price) VALUES (?, ?, ?, ?, ?, ?) '); foreach ($input['items'] as $item) { $stmt->execute([ $orderId, $item['product_id'], $item['product_name'], $item['quantity'], $item['price'], $item['price'] * $item['quantity'] ]); } $this->conn->commit(); $this->sendResponse([ 'success' => true, 'data' => [ 'order_id' => $orderId, 'total_amount' => $totalAmount ], 'message' => 'Order created successfully' ]); } catch (Exception $e) { $this->conn->rollBack(); $this->sendError('Failed to create order: ' . $e->getMessage(), 500); } } /** * Bestellung abrufen */ public function getOrder($id) { try { $stmt = $this->conn->prepare(' SELECT * FROM ws_order WHERE id = ? '); $stmt->execute([$id]); $order = $stmt->fetchAssociative(); if (!$order) { $this->sendError('Order not found', 404); } // Bestellpositionen laden $stmt = $this->conn->prepare(' SELECT * FROM ws_order_item WHERE order_id = ? '); $stmt->execute([$id]); $items = $stmt->fetchAllAssociative(); $order['items'] = $items; $this->sendResponse([ 'success' => true, 'data' => $order ]); } catch (Exception $e) { $this->sendError('Failed to fetch order: ' . $e->getMessage(), 500); } } /** * Bestellstatus aktualisieren */ public function updateOrderStatus($id) { if ($_SERVER['REQUEST_METHOD'] !== 'PUT') { $this->sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (empty($input['status'])) { $this->sendError('Status is required', 400); } $allowedStatuses = ['pending', 'confirmed', 'shipped', 'delivered', 'cancelled']; if (!in_array($input['status'], $allowedStatuses)) { $this->sendError('Invalid status', 400); } try { $stmt = $this->conn->prepare(' UPDATE ws_order SET status = ?, updated_at = NOW() WHERE id = ? '); $stmt->execute([$input['status'], $id]); if ($stmt->rowCount() === 0) { $this->sendError('Order not found', 404); } $this->sendResponse([ 'success' => true, 'message' => 'Order status updated successfully' ]); } catch (Exception $e) { $this->sendError('Failed to update order status: ' . $e->getMessage(), 500); } } /** * Kunde erstellen */ public function createCustomer() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } $required = ['email', 'first_name', 'last_name']; foreach ($required as $field) { if (empty($input[$field])) { $this->sendError("Missing required field: $field", 400); } } if (!filter_var($input['email'], FILTER_VALIDATE_EMAIL)) { $this->sendError('Invalid email address', 400); } try { // Prüfen ob E-Mail bereits existiert $stmt = $this->conn->prepare('SELECT id FROM ws_customer WHERE email = ?'); $stmt->execute([$input['email']]); $existing = $stmt->fetchAssociative(); if ($existing) { $this->sendError('Email already exists', 409); } // Kunde erstellen $stmt = $this->conn->prepare(' INSERT INTO ws_customer (email, first_name, last_name, phone, newsletter, active, created_at) VALUES (?, ?, ?, ?, ?, 1, NOW()) '); $stmt->execute([ $input['email'], $input['first_name'], $input['last_name'], $input['phone'] ?? '', isset($input['newsletter']) ? 1 : 0 ]); $customerId = $this->conn->lastInsertId(); $this->sendResponse([ 'success' => true, 'data' => [ 'customer_id' => $customerId, 'email' => $input['email'] ], 'message' => 'Customer created successfully' ]); } catch (Exception $e) { $this->sendError('Failed to create customer: ' . $e->getMessage(), 500); } } /** * API-Statistiken */ public function getStats() { try { // Produkt-Statistiken $stmt = $this->conn->prepare('SELECT COUNT(*) as total FROM ws_product WHERE active = 1'); $stmt->execute(); $productCount = $stmt->fetchAssociative()['total']; // Kategorie-Statistiken $stmt = $this->conn->prepare('SELECT COUNT(*) as total FROM ws_category WHERE active = 1'); $stmt->execute(); $categoryCount = $stmt->fetchAssociative()['total']; // Bestellungs-Statistiken $stmt = $this->conn->prepare('SELECT COUNT(*) as total FROM ws_order'); $stmt->execute(); $orderCount = $stmt->fetchAssociative()['total']; // Kunden-Statistiken $stmt = $this->conn->prepare('SELECT COUNT(*) as total FROM ws_customer WHERE active = 1'); $stmt->execute(); $customerCount = $stmt->fetchAssociative()['total']; $this->sendResponse([ 'success' => true, 'data' => [ 'products' => $productCount, 'categories' => $categoryCount, 'orders' => $orderCount, 'customers' => $customerCount ] ]); } catch (Exception $e) { $this->sendError('Failed to fetch statistics: ' . $e->getMessage(), 500); } } /** * Erfolgreiche Antwort senden */ private function sendResponse($data, $statusCode = 200) { http_response_code($statusCode); header('Content-Type: application/json'); echo json_encode($data, JSON_UNESCAPED_UNICODE); exit; } /** * Fehler-Antwort senden */ private function sendError($message, $statusCode = 400) { http_response_code($statusCode); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'error' => $message, 'status_code' => $statusCode ], JSON_UNESCAPED_UNICODE); exit; } }