Newwebshop/app/API/controllers/ApiController.php

541 lines
16 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* API-Controller für das Webshop-System
*
* @author Webshop System
* @license GPL v3
*/
namespace App\API\Controllers;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
class ApiController
{
private $conn;
private $apiKey;
private $allowedOrigins = ['*'];
public function __construct()
{
$this->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;
}
}