292 lines
14 KiB
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';
|
|
?>
|