Current File : /var/www/maausk-app/public/products_list.php |
<?php
// products_list.php — listado de productos con paginación, filtros y ordenación
// Mostrar errores en desarrollo
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Iniciar sesión si no está activa
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
require_once __DIR__ . '/db.php';
// Verificar sesión
if (!isset($_SESSION['user_email'])) {
header('Location: /index.php');
exit;
}
// Parámetros de orden y paginación
$allowedSort = ['id', 'reference', 'name', 'price', 'initial_quantity'];
$sort = $_GET['sort'] ?? 'name';
if (!in_array($sort, $allowedSort, true)) {
$sort = 'name';
}
$dir = strtolower($_GET['dir'] ?? 'asc');
if ($dir !== 'asc' && $dir !== 'desc') {
$dir = 'asc';
}
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 20;
$offset = ($page - 1) * $perPage;
// Filtros de búsqueda
$filters = [];
$params = [];
if (isset($_GET['name']) && $_GET['name'] !== '') {
$filters[] = 'p.name LIKE ?';
$params[] = '%' . $_GET['name'] . '%';
}
if (isset($_GET['reference']) && $_GET['reference'] !== '') {
$filters[] = 'p.reference LIKE ?';
$params[] = '%' . $_GET['reference'] . '%';
}
if (isset($_GET['price_min']) && is_numeric($_GET['price_min'])) {
$filters[] = 'p.price >= ?';
$params[] = (float) $_GET['price_min'];
}
if (isset($_GET['price_max']) && is_numeric($_GET['price_max'])) {
$filters[] = 'p.price <= ?';
$params[] = (float) $_GET['price_max'];
}
if (isset($_GET['quantity_min']) && is_numeric($_GET['quantity_min'])) {
$filters[] = 'p.initial_quantity >= ?';
$params[] = (int) $_GET['quantity_min'];
}
if (isset($_GET['quantity_max']) && is_numeric($_GET['quantity_max'])) {
$filters[] = 'p.initial_quantity <= ?';
$params[] = (int) $_GET['quantity_max'];
}
$whereSql = '';
if ($filters) {
$whereSql = 'WHERE ' . implode(' AND ', $filters);
}
// Debug (quitar en producción)
echo '<!-- WHERE: ' . $whereSql . ' -->';
echo '<!-- PARAMS: ' . implode(', ', array_map('htmlspecialchars', $params)) . ' -->';
// Contar total de registros
$countSql = "SELECT COUNT(*) FROM products p $whereSql";
$countStmt = $pdo->prepare($countSql);
$countStmt->execute($params);
$total = (int)$countStmt->fetchColumn();
$totalPages = (int)ceil($total / $perPage);
// Consulta principal con límites interpolados (offset y limit son enteros validados)
$sql = sprintf(
"SELECT p.id, p.reference, p.name, p.price, p.initial_quantity
FROM products p
%s
ORDER BY %s %s
LIMIT %d, %d",
$whereSql,
$sort,
$dir,
$offset,
$perPage
);
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$products = $stmt->fetchAll();
// Función para generar enlaces de ordenación
function sortLink($column, $label) {
global $sort, $dir;
$newDir = ($sort === $column && $dir === 'asc') ? 'desc' : 'asc';
$icon = '';
if ($sort === $column) {
$icon = $dir === 'asc' ? ' ↑' : ' ↓';
}
$qs = $_GET;
$qs['sort'] = $column;
$qs['dir'] = $newDir;
$url = '?' . http_build_query($qs);
return "<a href='".htmlspecialchars($url)."'>".htmlspecialchars($label)."{$icon}</a>";
}
?>
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Listado de Productos | Pharmacius</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/style.css" rel="stylesheet">
</head>
<body class="d-flex">
<?php require_once __DIR__ . '/sidebar.php'; ?>
<main id="content" class="flex-grow-1 p-4">
<h2 class="mb-4">Listado de Productos</h2>
<!-- Filtros de búsqueda -->
<form method="get" class="row g-3 mb-4">
<div class="col-md-3">
<label class="form-label">Nombre</label>
<input type="text" name="name" value="<?= htmlspecialchars($_GET['name'] ?? '') ?>" class="form-control" placeholder="Buscar nombre">
</div>
<div class="col-md-2">
<label class="form-label">Referencia</label>
<input type="text" name="reference" value="<?= htmlspecialchars($_GET['reference'] ?? '') ?>" class="form-control" placeholder="Buscar ref.">
</div>
<div class="col-md-2">
<label class="form-label">Precio Min</label>
<input type="number" step="0.01" name="price_min" value="<?= htmlspecialchars($_GET['price_min'] ?? '') ?>" class="form-control">
</div>
<div class="col-md-2">
<label class="form-label">Precio Máx</label>
<input type="number" step="0.01" name="price_max" value="<?= htmlspecialchars($_GET['price_max'] ?? '') ?>" class="form-control">
</div>
<div class="col-md-3 d-flex align-items-end">
<button class="btn btn-primary me-2">Buscar</button>
<a href="products_list.php" class="btn btn-secondary">Reiniciar</a>
</div>
</form>
<?php if (empty($products)): ?>
<div class="alert alert-info">No hay productos que coincidan.</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-light">
<tr>
<th><?= sortLink('id','ID') ?></th>
<th><?= sortLink('reference','Referencia') ?></th>
<th><?= sortLink('name','Nombre') ?></th>
<th><?= sortLink('price','Precio (€)') ?></th>
<th><?= sortLink('initial_quantity','Cantidad') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($products as $prod): ?>
<tr>
<td><?= htmlspecialchars($prod['id']) ?></td>
<td><?= htmlspecialchars($prod['reference']) ?></td>
<td><?= htmlspecialchars($prod['name']) ?></td>
<td><?= number_format($prod['price'],2) ?></td>
<td><?= (int)$prod['initial_quantity'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Paginación -->
<nav aria-label="Paginación" class="mt-4">
<ul class="pagination">
<?php if ($page > 1): ?>
<?php $qs = $_GET; $qs['page']=$page-1; ?>
<li class="page-item"><a class="page-link" href="?<?= http_build_query($qs) ?>">Anterior</a></li>
<?php endif; ?>
<?php for ($p=1; $p<=$totalPages; $p++): ?>
<?php $qs['page']=$p; ?>
<li class="page-item <?= $p=== $page?'active':'' ?>">
<a class="page-link" href="?<?= http_build_query($qs) ?>"><?= $p ?></a>
</li>
<?php endfor; ?>
<?php if ($page < $totalPages): ?>
<?php $qs['page']=$page+1; ?>
<li class="page-item"><a class="page-link" href="?<?= http_build_query($qs) ?>">Siguiente</a></li>
<?php endif; ?>
</ul>
</nav>
<?php endif; ?>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>