Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

Commit 9cfdb89

Browse files
authored
Merge pull request #60 from TheRefraction/sort-products
Filter and sort products
2 parents 0d49293 + de15ae1 commit 9cfdb89

6 files changed

Lines changed: 151 additions & 18 deletions

File tree

src/app/controllers/ProductController.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ public function viewProducts() {
1515
require_once __DIR__ . "/../views/products.php";
1616
}
1717

18+
19+
public function filterProducts() {
20+
$data = json_decode(file_get_contents("php://input"), true);
21+
22+
$search = $data['search'] ?? '';
23+
$sort = $data['sort'] ?? '';
24+
25+
$products = $this->productModel->getFiltered($search, $sort);
26+
27+
require __DIR__ . "/../views/partials/product_list.php";
28+
}
29+
1830
public function viewSingleProduct() {
1931
if($_SERVER['REQUEST_METHOD'] !== 'GET') {
2032
header('Location: /products');

src/app/models/Product.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,31 @@ public function getProductById($id) {
9292
return $stmt->fetch(PDO::FETCH_OBJ);
9393
}
9494

95+
public function getFiltered($search, $sort) {
96+
$query = "SELECT * FROM product p WHERE 1";
97+
98+
if(!empty($search)) {
99+
$query .= " AND name like :search";
100+
}
101+
102+
if(!empty(search)) {
103+
if($sort == "name_desc") {
104+
$query .= " ORDER BY name DESC";
105+
} else if ($sort == "name_asc") {
106+
$query .= " ORDER BY name ASC";
107+
}
108+
}
109+
110+
111+
$stmt = $this->conn->prepare($query);
112+
if(!empty($search)) {
113+
$stmt->bindValue(":search", "%$search%");
114+
}
115+
116+
$stmt->execute();
117+
return $stmt->fetchAll(PDO::FETCH_OBJ);
118+
}
119+
95120
/**
96121
* Reusable product details used by admin edit forms.
97122
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php foreach ($products as $product) { ?>
2+
<?php $imagePath = !empty($product->image) ? $product->image : '/assets/images/test.jpg'; ?>
3+
<article class="product-item">
4+
<a href="/product?id=<?= htmlspecialchars($product->id); ?>">
5+
<img
6+
src="<?= htmlspecialchars($imagePath); ?>"
7+
alt="<?= htmlspecialchars($product->name); ?>"
8+
>
9+
10+
<div class="product-info">
11+
<h2><?= htmlspecialchars($product->name); ?></h2>
12+
<p><?= htmlspecialchars($product->price); ?>€</p>
13+
</div>
14+
</a>
15+
</article>
16+
<?php } ?>

src/app/views/products.php

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,50 @@
44
<h1>Product list</h1>
55
<!--FIXME: fix category table, to organise products by category (those seen on the menu)-->
66

7-
<div>
8-
<?php foreach ($products as $product) { ?>
9-
<?php $imagePath = !empty($product->image) ? $product->image : '/assets/images/test.jpg'; ?>
10-
<article>
11-
<a href="/product?id=<?php echo htmlspecialchars($product->id); ?>">
12-
<img
13-
src="<?php echo htmlspecialchars($imagePath); ?>"
14-
alt="<?php echo htmlspecialchars($product->name); ?>"
15-
>
16-
17-
<div>
18-
<h2><?php echo htmlspecialchars($product->name); ?></h2>
19-
<p><?php echo htmlspecialchars($product->price); ?>€</p>
20-
</div>
21-
</a>
22-
</article>
23-
<?php } ?>
7+
<div class="sort-filter">
8+
<input type="text" id="search" placeholder="Rechercher...">
9+
<select id="sort">
10+
<option value="">-- Trier --</option>
11+
<option value="name_asc">Nom A-Z</option>
12+
<option value="name_desc">Nom Z-A</option>
13+
</select>
14+
</div>
15+
16+
<div class="products">
17+
<?php include "partials/product_list.php"; ?>
2418
</div>
2519
</main>
2620

27-
<?php include 'partials/footer.php'; ?>
21+
<?php include 'partials/footer.php'; ?>
22+
23+
24+
<script>
25+
function loadProducts() {
26+
const search = document.getElementById('search').value;
27+
const sort = document.getElementById('sort').value;
28+
29+
fetch('/products/filter', {
30+
method: 'POST',
31+
headers: { 'Content-Type': 'application/json' },
32+
body: JSON.stringify({ search, sort })
33+
})
34+
.then(res => res.text())
35+
.then(html => {
36+
document.querySelector('.products').innerHTML = html;
37+
});
38+
}
39+
40+
// events
41+
document.getElementById('search').addEventListener('input', debounce(loadProducts, 300));
42+
document.getElementById('sort').addEventListener('change', loadProducts);
43+
44+
// debounce
45+
function debounce(fn, delay) {
46+
let timeout;
47+
return (...args) => {
48+
clearTimeout(timeout);
49+
timeout = setTimeout(() => fn(...args), delay);
50+
};
51+
}
52+
53+
</script>

src/public/assets/css/style.css

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,52 @@
1414
color: white !important;
1515
transition: all 0.3s ease;
1616
}
17+
18+
h1 {
19+
padding: 10px;
20+
}
21+
22+
.products {
23+
display:flex;
24+
flex-wrap: wrap;
25+
justify-content: space-around;
26+
}
27+
28+
.product-item {
29+
margin: 25px;
30+
width: 250px;
31+
height: 350px;
32+
display: flex;
33+
flex-direction: column;
34+
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
35+
transition: transform 0.3s ease-in-out;
36+
37+
}
38+
39+
.product-item:hover {
40+
transform: scale(1.05);
41+
}
42+
43+
.product-item a {
44+
width: 100%;
45+
height: 100%;
46+
display: block;
47+
text-decoration: none;
48+
color: #222;
49+
}
50+
51+
.product-item img {
52+
height: 70%;
53+
width: 100%;
54+
object-fit: cover;
55+
}
56+
57+
.product-info {
58+
width: 100%;
59+
padding: 10px;
60+
}
61+
62+
.sort-filter {
63+
padding-left: 10px;
64+
padding-bottom:25px;
65+
}

src/routes/web.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@
153153
'action' => 'updateAccount',
154154
'access' => ['client']
155155
],
156+
'/products/filter' => [
157+
'controller' => 'ProductController',
158+
'action' => 'filterProducts',
159+
'access' => ['guest', 'client']
160+
],
156161
'/cart' => [
157162
'controller' => 'CartController',
158163
'action' => 'cartAction',

0 commit comments

Comments
 (0)