Geizkragen/compare.php

292 lines
14 KiB
PHP

<?php
/**
* @file compare.php
* @brief Skript zur Darstellung und Verwaltung des Produktvergleichs.
*
* @details Diese Datei rendert die Produktvergleichsseite. Sie ermöglicht dem Nutzer,
* Produkte aus derselben Kategorie nebeneinander tabellarisch aufzulisten, um ihre
* Eigenschaften (Attribute, Beschreibungen etc.) zu vergleichen. Produkte, die
* zuvor zur Session-Variable 'compare' hinzugefügt wurden, werden hier ausgelesen,
* ihre Daten und zugehörigen Attribute aus der Datenbank geladen und übersichtlich
* dargestellt. Zudem bietet die Seite die Funktionalität, einzelne Produkte wieder
* aus dem Vergleich zu entfernen.
*
* Beinhaltet:
* - Session-Management für den Produktvergleich.
* - Datenbankabfragen für Kategorien, Produkte und Attribute.
* - HTML-Generierung der Vergleichstabellen.
*
* @author Geizkragen-Team
* @date 2026-04-03
*/
require_once __DIR__ . '/lib/bootstrap.php';
/**
* @var mysqli $conn Die globale Datenbankverbindung, die durch db_connect() erstellt wird.
*/
$conn = db_connect();
/**
* @var string $title Der Titel der Webseite, der im Header angezeigt wird.
*/
$title = "Produktvergleich | Geizkragen";
// Einbinden der Header-Datei für das Layout und die Navigation.
include 'header.php';
/**
* @brief Behandelt das Entfernen eines Produkts aus dem Vergleich.
*
* Überprüft, ob ein POST-Request vorliegt und das Flag 'remove_compare' gesetzt ist.
* Ist dies der Fall, wird die übergebene Produkt-ID aus der Session-Variable
* in allen Kategorien entfernt.
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['remove_compare'])) {
/**
* @var int $removeId Die zu entfernende Produkt-ID, sicher in einen Integer umgewandelt.
*/
$removeId = (int)$_POST['remove_compare_id'];
// Prüfen, ob die Session-Variable für Vergleiche existiert und ein Array ist.
if (isset($_SESSION['compare']) && is_array($_SESSION['compare'])) {
// Durchsuchen aller Kategorien in der Session nach dem zu entfernenden Produkt.
foreach ($_SESSION['compare'] as $cat => &$prodList) {
/**
* @var int|false $key Der Array-Index der Produkt-ID in der aktuellen Kategorie-Liste.
*/
$key = array_search($removeId, $prodList);
if ($key !== false) {
// Das Produkt wurde gefunden und wird aus dem Array entfernt.
unset($prodList[$key]);
}
}
// Referenz auf $prodList löschen, um unbeabsichtigte Nebeneffekte zu vermeiden.
unset($prodList);
}
}
?>
<div class="container compare-page">
<h1 class="compare-title">Produktvergleich</h1>
<?php
/**
* @var bool $hasProducts Flag, welches angibt, ob aktuell Produkte für den Vergleich vorhanden sind.
*/
$hasProducts = false;
// Iteration über die Session-Variable 'compare', sofern diese existiert und gültig ist.
if (isset($_SESSION['compare']) && is_array($_SESSION['compare'])) {
foreach ($_SESSION['compare'] as $categoryId => $productIds) {
// Wenn die Liste der Produkte für diese Kategorie leer ist, überspringen wir sie.
if (empty($productIds)) continue;
// Setze Flag auf true, da mindestens ein zu vergleichendes Produkt existiert.
$hasProducts = true;
/**
* @var string $catName Der Standardname der Kategorie (wird überschrieben, falls in der DB gefunden).
*/
$catName = "Kategorie $categoryId";
/**
* @brief Holt den echten Namen der Kategorie aus der Datenbank.
*/
$stmtCat = $conn->prepare("SELECT name FROM categories WHERE categoryID = ?");
if ($stmtCat) {
$stmtCat->bind_param("i", $categoryId);
$stmtCat->execute();
/** @var mysqli_result $resCat Das Ergebnis der Abfrage des Kategorienamens. */
$resCat = $stmtCat->get_result();
if ($row = $resCat->fetch_assoc()) {
$catName = $row['name'];
}
$stmtCat->close();
}
/**
* @var array $attributes Sammelt alle Attribute (Eigenschaften), die zu dieser Kategorie gehören.
* Format: $attributes[attributeID] = [ 'attributeID' => ..., 'name' => ..., 'unit' => ..., 'dataType' => ... ]
*/
$attributes = [];
/**
* @brief Holt die Kategorie-Attribute mit Namen, Einheit und Datentyp.
*/
$stmtAttr = $conn->prepare("
SELECT a.attributeID, a.name, a.unit, a.dataType
FROM categoryAttributes ca
JOIN attributes a ON ca.attributeID = a.attributeID
WHERE ca.categoryID = ?
ORDER BY a.attributeID
");
if ($stmtAttr) {
$stmtAttr->bind_param("i", $categoryId);
$stmtAttr->execute();
/** @var mysqli_result $resAttr Das Ergebnis der Abfrage der Kategorie-Attribute. */
$resAttr = $stmtAttr->get_result();
while ($row = $resAttr->fetch_assoc()) {
$attributes[$row['attributeID']] = $row;
}
$stmtAttr->close();
}
/**
* @var array $products Sammelt die Grunddaten der zugehörigen Produkte (Model, Bild, Beschreibung).
* Format: $products[productID] = [ ... Produktdaten ... ]
*/
$products = [];
/**
* @var string $idList Kommaseparierte Liste der zu vergleichenden Produkt-IDs, escapet als Integers für den IN-Query.
*/
$idList = implode(',', array_map('intval', $productIds));
/**
* @brief Holt die Produktinformationen für die IN-Klausel Liste.
*/
$stmtProd = $conn->query("SELECT productID, model, imagePath, description FROM products WHERE productID IN ($idList)");
if ($stmtProd) {
while ($row = $stmtProd->fetch_assoc()) {
$products[$row['productID']] = $row;
}
}
/**
* @var array $productAttrVals Sammelt die spezifischen Attribut-Werte je Produkt.
* Format: $productAttrVals[productID][attributeID] = [ ... Attributwerte ... ]
*/
$productAttrVals = [];
/**
* @brief Holt die Werte (String, Number, Bool) der Attribute für die ausgewählten Produkte.
*/
$stmtProdAttr = $conn->query("SELECT productID, attributeID, valueString, valueNumber, valueBool FROM productAttributes WHERE productID IN ($idList)");
if ($stmtProdAttr) {
while ($row = $stmtProdAttr->fetch_assoc()) {
$productAttrVals[$row['productID']][$row['attributeID']] = $row;
}
$stmtProdAttr->close();
}
?>
<!-- Ausgabe des Kategorie-Titels -->
<h2 class="compare-category-title"><?= htmlspecialchars($catName) ?></h2>
<!-- Beginn der Vergleichstabelle der aktuellen Kategorie -->
<div class="compare-table-wrapper">
<table class="compare-table">
<thead>
<tr>
<!-- Die erste Spalte enthält den Titel "Eigenschaft" -->
<th>Eigenschaft</th>
<!-- Iteration über die Produkt-IDs zur Erzeugung der Spaltenköpfe -->
<?php foreach ($productIds as $pId): ?>
<?php
// Wenn das Produkt in der DB nicht (mehr) existiert, überspringen.
if (!isset($products[$pId])) continue;
?>
<th>
<div>
<!-- Produktbild mit Link zur Produktdetailseite -->
<a href="productpage.php?id=<?= $pId ?>">
<img src="<?= htmlspecialchars($products[$pId]['imagePath'] ?? 'assets/images/placeholder.png') ?>" alt="Produktbild" class="compare-product-img">
</a>
</div>
<!-- Produktname mit Link zur Produktdetailseite -->
<h3><a href="productpage.php?id=<?= $pId ?>" class="compare-product-name"><?= htmlspecialchars($products[$pId]['model']) ?></a></h3>
<!-- Formular zum Entfernen dieses spezifischen Produkts aus dem Vergleich -->
<form method="POST" action="compare.php">
<input type="hidden" name="remove_compare" value="1">
<input type="hidden" name="remove_compare_id" value="<?= $pId ?>">
<button type="submit" class="compare-remove-btn">Entfernen</button>
</form>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<!-- Zeile für die Produktbeschreibung -->
<tr>
<td>Beschreibung</td>
<?php foreach ($productIds as $pId): ?>
<?php
// Auch hier prüfen, ob die Produktdaten valide sind
if (!isset($products[$pId])) continue;
?>
<td>
<!-- Ausgabe der Produktbeschreibung -->
<span class="desc-text"><?= htmlspecialchars($products[$pId]['description']) ?></span>
</td>
<?php endforeach; ?>
</tr>
<!-- Dynamische Iteration über alle dieser Kategorie zugeordneten Attribute -->
<?php foreach ($attributes as $attrId => $attr): ?>
<tr>
<!-- Ausgabe des Attributnamens -->
<td><?= htmlspecialchars($attr['name'] ?? '') ?></td>
<?php foreach ($productIds as $pId): ?>
<?php
// Existenzprüfung des Produkts
if (!isset($products[$pId])) continue;
?>
<td>
<?php
/**
* @brief Logik zur Darstellung des passenden Attributwerts.
*
* Prüft, welcher Datentyp / welches Feld im Datensatz gesetzt ist
* und wählt die angemessene Formatierung (inklusive Einheit bei Zahlen,
* 'Ja'/'Nein' bei Booleans).
*/
if (isset($productAttrVals[$pId][$attrId])) {
$valRow = $productAttrVals[$pId][$attrId];
// Fall: String-Wert vorhanden
if (!empty($valRow['valueString'])) {
echo htmlspecialchars($valRow['valueString'] ?? '');
}
// Fall: Numerischer Wert vorhanden. '0.00' oder 0 ist ebenfalls gültig.
elseif (!empty($valRow['valueNumber']) || $valRow['valueNumber'] === '0.00' || $valRow['valueNumber'] === 0) {
echo htmlspecialchars((string)floatval($valRow['valueNumber'])) . " " . htmlspecialchars($attr['unit'] ?? '');
}
// Fall: Boolescher Wert (wurde in DB gesetzt, nicht null)
elseif ($valRow['valueBool'] !== null) {
echo $valRow['valueBool'] ? 'Ja' : 'Nein';
}
// Kein verwertbarer Wert vorhanden, Ausgabe eines Platzhalters.
else {
echo '-';
}
} else {
// Wenn zu diesem Produkt kein Datensatz für dieses Attribut gefunden wurde.
echo '-';
}
?>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php
}
}
// Meldung falls keine Produkte für den Vergleich gesetzt sind.
if (!$hasProducts) {
echo "<div class='compare-empty'><p>Du hast noch keine Produkte zum Vergleich hinzugefügt. Gehe auf eine Produktseite, um Produkte hinzuzufügen.</p></div>";
}
?>
</div>
<?php
// Einbinden der Footer-Datei am Ende der Seite.
include 'footer.php';
?>