Geizkragen/attrbar.php

234 lines
10 KiB
PHP

<?php
/**
* @file attrbar.php
* @brief Inkludierte Datei für die Darstellung und Verarbeitung der Attribut-Filterleiste (attrbar).
*
* @details Diese Datei generiert eine dynamische Filterleiste basierend auf der aktuell
* ausgewählten Produktkategorie. Sie stellt eine Verbindung zur Datenbank her, ruft
* zugehörige Attribute ab und generiert ein HTML-Formular zur Filterung der Produkte nach spezifischen Attributen.
* Das Senden des Formulars geschieht automatisch bei einer Änderung eines Auswahlfeldes (onchange) oder über
* einen Button (falls JavaScript deaktiviert ist).
*/
// Binden der benötigten Bootstrap-Datei ein, welche grundlegende Konfigurationen und Hilfsfunktionen zur Verfügung stellt.
require_once __DIR__ . '/lib/bootstrap.php';
/**
* @var mysqli $conn
* @brief Stellt eine Verbindung zur Datenbank her.
*/
$conn = db_connect();
/**
* @var string $currentCategory
* @brief Speichert die aktuell gesetzte Kategorie aus dem GET-Parameter. Standardwert ist 'all'.
*/
$currentCategory = isset($_GET['category']) ? $_GET['category'] : 'all';
/**
* @var int|null $catId
* @brief Beinhaltet die numerische ID der aktuellen Kategorie zur Verwendung in Datenbankabfragen.
*/
$catId = null;
/**
* @var array $categoriesConfig
* @brief Ein Mapping-Array, welches Kategorie-Slugs (Strings) auf deren entsprechende Datenbank-IDs (Integer) mappt.
*/
$categoriesConfig = [
'iphone' => 20,
'ipad' => 21,
'macbook' => 22,
'airpods' => 23,
'accessories' => 24,
'watch' => 25,
];
// Überprüfen, ob die gewählte Kategorie im Konfigurations-Array vorhanden ist.
if (isset($categoriesConfig[$currentCategory])) {
// Falls vorhanden, wird die entsprechende Kategorie-ID für weitere Abfragen gesetzt.
$catId = $categoriesConfig[$currentCategory];
}
/**
* @var array $attributes
* @brief Speichert die verfügbaren Attribute (z.B. Farbe, Speichergröße) für die selektierte Kategorie.
*/
$attributes = [];
// Attribute nur laden, wenn eine gültige Kategorie-ID gefunden wurde.
if ($catId) {
/**
* @details Holt alle Attribute, die mit der aktiven Kategorie verknüpft sind,
* sortiert nach ihrem Namen, um eine geordnete Filterliste anzuzeigen.
*/
$stmtAttr = $conn->prepare("
SELECT a.attributeID, a.name, a.unit, a.dataType
FROM attributes a
JOIN categoryAttributes ca ON a.attributeID = ca.attributeID
WHERE ca.categoryID = ?
ORDER BY a.name
");
$stmtAttr->bind_param("i", $catId);
$stmtAttr->execute();
$resAttr = $stmtAttr->get_result();
// Iteriere über das Result-Set und speichere jedes gefundene Attribut im $attributes-Array.
while ($row = $resAttr->fetch_assoc()) {
$attributes[] = $row;
}
// Statement schließen, um Ressourcen freizugeben.
$stmtAttr->close();
}
?>
<?php
// Wenn Attribute existieren, wird der HTML-Container für die Filterleiste gerendert.
if (!empty($attributes)): ?>
<div class="attrbar" aria-label="Attributfilter">
<div class="attrbar__inner container">
<!-- Das Formular sendet Daten per GET an die index.php für die Filterung -->
<form action="index.php" method="GET" class="attr-filter-form">
<?php
// Aktuelle Kategorie-ID im Formular versteckt mitsenden, damit man in der Kategorie bleibt.
if (isset($_GET['category'])): ?>
<input type="hidden" name="category" value="<?= htmlspecialchars($_GET['category']) ?>">
<?php endif; ?>
<?php
// Einen eventuellen Suchbegriff ebenfalls beibehalten.
if (isset($_GET['search'])): ?>
<input type="hidden" name="search" value="<?= htmlspecialchars($_GET['search']) ?>">
<?php endif; ?>
<?php
/**
* @details Iteriert durch jedes gefundene Attribut, um ein entsprechendes
* HTML-Select-Feld mit seinen eindeutigen Werten zu generieren.
*/
foreach ($attributes as $attr):
/** @var int $attrId Die Datenbank-ID des Attributs */
$attrId = $attr['attributeID'];
/** @var string $attrName Setzt sich zusammen aus dem Namen und, falls vorhanden, der Einheit (z.B. GB) */
$attrName = $attr['name'] . ($attr['unit'] ? ' (' . $attr['unit'] . ')' : '');
// SQL-Abfrage vorbereiten, um alle unterschiedlichen (DISTINCT) Werte für genau dieses Attribut abzufragen.
if ($catId) {
// Abfrage mit Kategorie-Einschränkung
$vStmt = $conn->prepare("
SELECT DISTINCT pa.valueString, pa.valueNumber, pa.valueBool
FROM productAttributes pa
JOIN products p ON pa.productID = p.productID
WHERE p.categoryID = ? AND pa.attributeID = ?
");
$vStmt->bind_param("ii", $catId, $attrId);
} else {
// Alternative Abfrage (wird in diesem Skriptteil zwar aktuell nur bei $catId aufgerufen,
// aber dient als Fallback für zukünftige globale Filter).
$vStmt = $conn->prepare("
SELECT DISTINCT valueString, valueNumber, valueBool
FROM productAttributes
WHERE attributeID = ?
");
$vStmt->bind_param("i", $attrId);
}
$vStmt->execute();
$vRes = $vStmt->get_result();
/**
* @var array $values
* @brief Speichert die extrahierten eindeutigen Werte (bereinigt und formatiert) für die Select-Box.
*/
$values = [];
// Durchlaufe alle gefundenen Attributwerte für die aktuellen Produkte
while ($vRow = $vRes->fetch_assoc()) {
// Werte je nach Datentyp (Boolean, Number, String) extrahieren
if ($attr['dataType'] === 'boolean' || $vRow['valueBool'] !== null) {
$val = $vRow['valueBool'] ? 'Ja' : 'Nein';
// Duplikate vermeiden
if (!in_array($val, $values)) $values[] = $val;
} elseif ($attr['dataType'] === 'number' || $vRow['valueNumber'] !== null) {
$val = $vRow['valueNumber'];
// Entfernt überflüssige Nullen nach dem Komma (z.B. wird 5.00 zu 5)
$val = rtrim(rtrim((string)$val, '0'), '.');
if (!in_array($val, $values)) $values[] = $val;
} elseif ($vRow['valueString'] !== null) {
$val = $vRow['valueString'];
if (!in_array($val, $values)) $values[] = $val;
}
}
$vStmt->close();
/**
* @details Sortiert die gesammelten Werte.
* Zahlen werden numerisch sortiert (damit z.B. 10 nach 2 kommt),
* Strings werden alphabetisch sortiert.
*/
if ($attr['dataType'] === 'number') {
usort($values, function($a, $b) { return (float)$a <=> (float)$b; });
} else {
sort($values);
}
// Wenn es keine Werte für das Filterfeld gibt, überspringe dieses Attribut.
if (empty($values)) continue;
/** @var string $paramName Der Name des GET-Parameters im Formular (z.B. attr_1) */
$paramName = "attr_" . $attrId;
/** @var string $selectedValue Der aktuell ausgewählte Wert aus dem GET-Parameter, falls gesetzt */
$selectedValue = isset($_GET[$paramName]) ? $_GET[$paramName] : '';
?>
<div class="attr-filter-item">
<!-- Label für das Attribut (z.B. Speichergröße (GB)) -->
<label for="attr_<?= $attrId ?>"><?= htmlspecialchars($attrName) ?>:</label>
<!-- Dropdown-Menu zur Auswahl eines Werts. onchange sumbittet das Formular sofort via JS. -->
<select name="<?= $paramName ?>" id="attr_<?= $attrId ?>" onchange="this.form.submit()">
<option value="">Alle</option>
<?php
// Liste alle aufbereiteten Optionen auf
foreach ($values as $val): ?>
<option value="<?= htmlspecialchars($val) ?>" <?= (string)$val === (string)$selectedValue ? 'selected' : '' ?>>
<?= htmlspecialchars($val) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endforeach; ?>
<!-- Fallback-Button, falls der Nutzer JavaScript in seinem Browser deaktiviert hat. -->
<noscript>
<button type="submit">Filtern</button>
</noscript>
<?php
/**
* @var bool $hasActiveFilter
* @brief Überprüft, ob mindestens ein Attributfilter aktiv in der URL vorkommt.
*/
$hasActiveFilter = false;
foreach ($_GET as $k => $v) {
// Wenn der GET-Key mit "attr_" beginnt und ein Wert gesetzt ist
if (strpos($k, 'attr_') === 0 && $v !== '') {
$hasActiveFilter = true;
break; // Sobald ein Filter gefunden ist, kann die Schleife abgebrochen werden
}
}
// Zeige den "Filter zurücksetzen"-Button nur an, wenn wirklich Filter aktiv sind.
if ($hasActiveFilter): ?>
<div class="attr-filter-action">
<!-- Ein Link auf die index.php (mit Kategorie), der alle Attribut-GET-Parameter verwirft -->
<a href="index.php<?= isset($_GET['category']) ? '?category='.urlencode($_GET['category']) : '' ?>" class="attr-filter-reset">Filter zurücksetzen</a>
</div>
<?php endif; ?>
</form>
</div>
</div>
<?php endif; ?>