You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP:基于筛选查询按参数分组数组及图书目录侧边栏功能

Implementing Filtering & Grouping for Your PHP Book Catalog

Alright, let's tackle this filtering and grouping logic for your PHP book catalog. You've already got your core classes (libro, editorial, materia) set up, so we can build right on top of those to handle sidebar filters and grouped book displays.

Step 1: Capture Filter Parameters

First, we'll grab the selected filters from the URL (using GET parameters, since filters are shareable links). We'll handle multiple selections for both publishers and categories, and make sure IDs are sanitized to integers.

// Get selected filter IDs from the request, default to empty arrays
$selectedEditorialIds = isset($_GET['editoriales']) ? array_map('intval', (array)$_GET['editoriales']) : [];
$selectedMateriaIds = isset($_GET['materias']) ? array_map('intval', (array)$_GET['materias']) : [];

Step 2: Filter Books Based on Selections

Next, we'll filter your full list of books ($allLibros, loaded from your XML) to only keep those that match the selected publishers/categories.

$filteredLibros = [];

foreach ($allLibros as $libro) {
    // Check if the book's publisher matches (or no publishers are selected)
    $matchesEditorial = empty($selectedEditorialIds) || in_array($libro->editorial->id, $selectedEditorialIds);
    
    // Check if the book belongs to any selected category (or no categories are selected)
    $matchesMateria = empty($selectedMateriaIds);
    if (!$matchesMateria) {
        foreach ($libro->materia as $materia) {
            if (in_array($materia->id, $selectedMateriaIds)) {
                $matchesMateria = true;
                break; // No need to check other categories once we find a match
            }
        }
    }
    
    // Keep the book only if both filters are satisfied
    if ($matchesEditorial && $matchesMateria) {
        $filteredLibros[] = $libro;
    }
}

Step 3: Group Filtered Books

Now we can group the filtered books by either publisher or category (or both, depending on your display needs).

Group by Publisher

$groupedByEditorial = [];

foreach ($filteredLibros as $libro) {
    $editorialId = $libro->editorial->id;
    
    // Initialize the group if it doesn't exist
    if (!isset($groupedByEditorial[$editorialId])) {
        $groupedByEditorial[$editorialId] = [
            'editorial' => $libro->editorial,
            'libros' => []
        ];
    }
    
    $groupedByEditorial[$editorialId]['libros'][] = $libro;
}

Group by Category (Handle Multi-Category Books)

Since a book can belong to multiple categories, we'll avoid adding the same book multiple times to the same category group:

$groupedByMateria = [];

foreach ($filteredLibros as $libro) {
    foreach ($libro->materia as $materia) {
        $materiaId = $materia->id;
        
        if (!isset($groupedByMateria[$materiaId])) {
            $groupedByMateria[$materiaId] = [
                'materia' => $materia,
                'libros' => []
            ];
        }
        
        // Only add the book if it's not already in this category group
        if (!in_array($libro, $groupedByMateria[$materiaId]['libros'], true)) {
            $groupedByMateria[$materiaId]['libros'][] = $libro;
        }
    }
}

Step 4: Build the Interactive Sidebar

We need to render the sidebar with publishers/categories, showing selected states and the number of matching books for each option. We'll also preserve existing filters when toggling a selection.

Publisher Sidebar Section

<div class="sidebar-section">
    <h3>Publishers</h3>
    <?php foreach ($allEditoriales as $editorial): ?>
        <?php
        // Calculate how many filtered books belong to this publisher
        $bookCount = count(array_filter($filteredLibros, function($libro) use ($editorial) {
            return $libro->editorial->id === $editorial->id;
        }));
        
        // Check if this publisher is selected
        $isSelected = in_array($editorial->id, $selectedEditorialIds);
        $activeClass = $isSelected ? 'sidebar-item--selected' : '';
        
        // Build the toggle URL (preserve other filters)
        $queryParams = $_GET;
        if ($isSelected) {
            // Remove the publisher from selected IDs
            $queryParams['editoriales'] = array_diff($queryParams['editoriales'], [$editorial->id]);
            if (empty($queryParams['editoriales'])) unset($queryParams['editoriales']);
        } else {
            // Add the publisher to selected IDs
            $queryParams['editoriales'] = isset($queryParams['editoriales']) 
                ? array_merge($queryParams['editoriales'], [$editorial->id]) 
                : [$editorial->id];
        }
        $toggleUrl = '?' . http_build_query($queryParams);
        ?>
        <a href="<?= $toggleUrl ?>" class="sidebar-item <?= $activeClass ?>">
            <?= htmlspecialchars($editorial->name) ?> (<?= $bookCount ?>)
        </a>
    <?php endforeach; ?>
</div>

Category Sidebar Section

This follows the same pattern as the publisher section—just swap out the editorial logic for categories:

<div class="sidebar-section">
    <h3>Categories</h3>
    <?php foreach ($allMaterias as $materia): ?>
        <?php
        $bookCount = count(array_filter($filteredLibros, function($libro) use ($materia) {
            foreach ($libro->materia as $bookMateria) {
                if ($bookMateria->id === $materia->id) return true;
            }
            return false;
        }));
        
        $isSelected = in_array($materia->id, $selectedMateriaIds);
        $activeClass = $isSelected ? 'sidebar-item--selected' : '';
        
        $queryParams = $_GET;
        if ($isSelected) {
            $queryParams['materias'] = array_diff($queryParams['materias'], [$materia->id]);
            if (empty($queryParams['materias'])) unset($queryParams['materias']);
        } else {
            $queryParams['materias'] = isset($queryParams['materias']) 
                ? array_merge($queryParams['materias'], [$materia->id]) 
                : [$materia->id];
        }
        $toggleUrl = '?' . http_build_query($queryParams);
        ?>
        <a href="<?= $toggleUrl ?>" class="sidebar-item <?= $activeClass ?>">
            <?= htmlspecialchars($materia->name) ?> (<?= $bookCount ?>)
        </a>
    <?php endforeach; ?>
</div>

Step 5: Render Grouped Books

Finally, display the filtered and grouped books. Here's an example of rendering books grouped by publisher:

<div class="book-catalog">
    <?php if (empty($filteredLibros)): ?>
        <p>No books match your current filters.</p>
    <?php else: ?>
        <?php foreach ($groupedByEditorial as $group): ?>
            <div class="book-group">
                <h4><?= htmlspecialchars($group['editorial']->name) ?></h4>
                <ul class="book-list">
                    <?php foreach ($group['libros'] as $libro): ?>
                        <li><?= htmlspecialchars($libro->name) ?></li>
                    <?php endforeach; ?>
                </ul>
            </div>
        <?php endforeach; ?>
    <?php endif; ?>
</div>

Key Notes for Optimization & Safety

  • Performance: If you have a large number of books, precompute counts (e.g., build a map of publisher IDs to book counts) instead of using array_filter in the sidebar loop—this reduces redundant iterations.
  • XSS Protection: Always use htmlspecialchars() when outputting user-provided data (like book/publisher names) to prevent cross-site scripting attacks.
  • Empty State: Don't forget to handle the case where no books match the filters, as shown above.
  • Parameter Sanitization: We used intval to clean filter IDs—make sure all user input is sanitized to avoid unexpected behavior.

内容的提问来源于stack exchange,提问作者cafequijano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:51:59