Geizkragen/compcards.php

291 lines
11 KiB
PHP

<?php
/**
* @file compcards.php
* @brief Datei compcards.php
*
* @details Diese Datei ist für die Anzeige der Produktkarten auf der Übersichts- oder Suchseite zuständig.
* Sie verarbeitet Suchanfragen sowie Kategorie- und Attributfilter und generiert die entsprechende HTML-Ausgabe.
* Es werden keine Änderungen am bestehenden Code vorgenommen, lediglich diese ausführlichen Kommentare hinzugefügt.
*/
// login.php
require_once __DIR__ . '/lib/bootstrap.php';
/**
* Fehleranzeige für Entwicklungszwecke aktivieren.
* Hiermit werden alle Fehler, Warnungen und Hinweise direkt ausgegeben.
*/
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
/**
* 1) DB-Verbindung (einmalig aufbauen)
* Die Funktion db_connect() muss in den eingebundenen Bibliotheken (z. B. bootstrap.php / db.php) definiert sein.
* @var mysqli $conn Die aktive Datenbankverbindung.
*/
$conn = db_connect();
?>
<?php
/**
* ─────────────────────────────────────────────
* Reine PHP-Suche (GET ?search=...)
* ─────────────────────────────────────────────
* @details Wenn ein Suchbegriff über den URL-Parameter 'search' übergeben wurde,
* zeigen wir ausschließlich die Suchergebnisse an und überspringen die Anzeige
* der standardmäßigen Kategorie-Sektionen.
*/
/**
* @var string $searchTerm Holt den Suchbegriff aus dem GET-Parameter, trimmt überflüssige Leerzeichen.
*/
$searchTerm = isset($_GET['search']) ? trim((string)$_GET['search']) : '';
/**
* @var int $searchLen Berechnet die Länge des Suchbegriffs (multibyte-safe, falls mb_strlen existiert).
*/
$searchLen = function_exists('mb_strlen') ? mb_strlen($searchTerm, 'UTF-8') : strlen($searchTerm);
if ($searchTerm !== '') {
/**
* @details Escaping des Suchbegriffs für die LIKE-Klausel, um SQL-Injections durch Wildcards zu verhindern.
* @var string $like Der für SQL präparierte Such-String mit umschließenden %-Zeichen.
*/
$like = addcslashes($searchTerm, "%_\\");
$like = '%' . $like . '%';
/**
* @details Führt eine Suche auf die Tabelle 'products' aus.
* Es wird sowohl in der Spalte 'model' als auch in 'description' gesucht.
* @var mysqli_stmt|false $stmtSearch Das Prepared Statement für die Suche.
*/
$stmtSearch = $conn->prepare("
SELECT productID, model, description, imagePath
FROM products
WHERE model LIKE ? OR description LIKE ?
ORDER BY model ASC
LIMIT 60
");
if ($stmtSearch) {
// Parameterbindung: Zwei Strings ('ss') für die doppelten LIKE-Bedingungen.
$stmtSearch->bind_param('ss', $like, $like);
$stmtSearch->execute();
/**
* @var mysqli_result $resultSearch Das ResultSet der ausgeführten Suchanfrage.
*/
$resultSearch = $stmtSearch->get_result();
?>
<section class="product-section">
<h2>Suchergebnisse für „<?= htmlspecialchars($searchTerm) ?>“</h2>
<?php if ($resultSearch->num_rows <= 0): ?>
<!-- Wenn keine Ergebnisse gefunden wurden -->
<p class="search-empty">Keine Produkte gefunden.</p>
<?php else: ?>
<!-- Grid für die Anzeige der Suchergebnisse -->
<div class="product-grid">
<?php while ($product = $resultSearch->fetch_assoc()): ?>
<?php
/**
* @var int $productId Casting der Produkt-ID auf Integer für den Link.
*/
$productId = (int)$product['productID'];
?>
<a class="product-card" href="productpage.php?id=<?= $productId ?>">
<!-- Anzeige des Produktbildes mit Fallback auf ein Platzhalter-Bild -->
<img
src="<?= !empty($product['imagePath']) ? htmlspecialchars($product['imagePath']) : 'assets/images/placeholder.png' ?>"
alt="<?= htmlspecialchars($product['model'] ?? '') ?>">
<div class="product-card__content">
<!-- Der Modellname des Produkts -->
<h3><?= htmlspecialchars($product['model'] ?? '') ?></h3>
<!-- Die Beschreibung des Produkts -->
<p><?= htmlspecialchars($product['description'] ?? '') ?></p>
</div>
</a>
<?php endwhile; ?>
</div>
<?php endif; ?>
</section>
<?php
// Schließen des Prepared Statements zur Freigabe von Ressourcen.
$stmtSearch->close();
}
/**
* Wichtig: Im Suchmodus beenden wir die Einbindung dieser Datei hier (return),
* damit keine weiteren Kategorie-Blöcke gerendert werden.
*/
return;
}
?>
<?php
/**
* @details Ermitteln der aktiven Kategorie. Falls keine angegeben wurde, ist 'all' der Standard.
* @var string $activeCategory Die aktive Kategorie-ID in Textform.
*/
$activeCategory = isset($_GET['category']) ? $_GET['category'] : 'all';
?>
<?php
/**
* @details Mapping von Kategorie-Keys zu Datenbank-IDs und lesbaren Labels.
* @var array $categories Ein assoziatives Array der verfügbaren Hauptkategorien.
*/
$categories = [
'iphone' => ['id' => 20, 'label' => 'iPhone'],
'ipad' => ['id' => 21, 'label' => 'iPad'],
'macbook' => ['id' => 22, 'label' => 'MacBook'],
'airpods' => ['id' => 23, 'label' => 'AirPods'],
'watch' => ['id' => 25, 'label' => 'Watch'],
'accessories' => ['id' => 24, 'label' => 'Accessories'],
];
?>
<?php
/**
* Iteration über alle definierten Kategorien, um jede als einzelnen Sektor anzuzeigen.
*/
foreach ($categories as $key => $cat):
?>
<?php
/**
* @details Nur dann Daten laden und Sektion anzeigen, wenn entweder alle Kategorien
* gewünscht sind oder der key mit der aktuell gewählten Kategorie übereinstimmt.
*/
if ($activeCategory === 'all' || $activeCategory === $key):
?>
<?php
/**
* @var string $baseQuery Der grundlegende SELECT-Teil der dynamischen Produktsuche.
*/
$baseQuery = "SELECT DISTINCT p.productID, p.model, p.description, p.imagePath FROM products p ";
/**
* @var array $whereClauses Array zum Sammeln aller WHERE-Bedingungen.
*/
$whereClauses = ["p.categoryID = ?"];
/**
* @var array $params Array zur Aufnahme der Bind-Parameter für das Prepared Statement.
*/
$params = [$cat['id']];
/**
* @var string $types Enthält den Typ-String für bind_param (z. B. 'i', 's', 'd').
*/
$types = "i";
/**
* @details Auslesen von dynamischen Attribut-Filtern aus der URL ($_GET).
* @var int $attrIndex Zähler zur Erzeugung eindeutiger Aliase für JOINs.
*/
$attrIndex = 0;
foreach ($_GET as $k => $v) {
// Nur Parameter berücksichtigen, die auf 'attr_' beginnen und einen Wert haben.
if ($v !== '' && strpos($k, 'attr_') === 0) {
$attrId = (int)substr($k, 5);
$attrAlias = "pa" . $attrIndex;
// Dynamischer JOIN der productAttributes-Tabelle für jedes gefilterte Attribut.
$baseQuery .= " JOIN productAttributes $attrAlias ON p.productID = $attrAlias.productID ";
/**
* @details Sucht im Attribut entweder nach String, Number oder Boolean ('Ja'/'Nein').
*/
$whereClauses[] = "($attrAlias.attributeID = ? AND ($attrAlias.valueString = ? OR $attrAlias.valueNumber = ? OR ($attrAlias.valueBool = 1 AND ? = 'Ja') OR ($attrAlias.valueBool = 0 AND ? = 'Nein')))";
// Parameter für bind_param befüllen
$params[] = $attrId;
$params[] = $v;
$params[] = is_numeric($v) ? (float)$v : 0;
$params[] = $v;
$params[] = $v;
// Typen ergänzen: Integer, String, Double, String, String an SQL übergeben
$types .= "isdss";
$attrIndex++;
}
}
/**
* @var string $sql Zusammensetzen der kompletten SQL-Abfrage aus Base, JOINs und WHEREs.
*/
$sql = $baseQuery . " WHERE " . implode(" AND ", $whereClauses);
/**
* @var mysqli_stmt $stmt Das Prepared Statement für die Kategorieabfrage.
*/
$stmt = $conn->prepare($sql);
// Bind Parameter per Spread-Operator aus dem Params-Array.
$stmt->bind_param($types, ...$params);
$stmt->execute();
/**
* @var mysqli_result $result Das Ergebnis-Set mit den gefundenen Produkten dieser Kategorie.
*/
$result = $stmt->get_result();
?>
<?php
/**
* Rendern der Produktsektion nur, falls auch Produkte in dieser Kategorie gefunden wurden.
*/
if ($result->num_rows > 0):
?>
<section class="product-section">
<!-- Ausgabe des Kategorie-Labels als Überschrift -->
<h2><?= htmlspecialchars($cat['label']) ?></h2>
<!-- Horizontaler Scroll-Bereich für die Produktkarten einer Kategorie -->
<div class="product-scroll">
<?php
/**
* Fetch-Schleife über jedes Produkt im Result-Set.
*/
while ($product = $result->fetch_assoc()):
?>
<?php
/**
* @var int $productId Casting der Produkt-ID
*/
$productId = (int)$product['productID'];
?>
<a class="product-card" href="productpage.php?id=<?= $productId ?>">
<!-- Produktbild und Fallback auf Platzhalter -->
<img
src="<?= isset($product['imagePath']) ? $product['imagePath'] : 'assets/images/placeholder.png' ?>"
alt="<?= htmlspecialchars($product['model']) ?>">
<div class="product-card__content">
<!-- Anzeige des Produktnamens -->
<h3><?= htmlspecialchars($product['model']) ?></h3>
<!-- Anzeige der Produktbeschreibung -->
<p><?= htmlspecialchars($product['description']) ?></p>
</div>
</a>
<?php endwhile; ?>
</div>
</section>
<?php endif; ?>
<?php
// Schließen des Prepared Statements der Kategorie, um Ressourcen freizugeben.
$stmt->close();
?>
<?php endif; ?>
<?php endforeach; ?>