Hi, I’ve been cracking my head on this problem for a while now. I’m building a site for a wine merchant. He has a lot of varieties that have the following properties that can be set in the backend:
- estate (type: select w/ query)
- kind (type: tags)
- grape (type: tags)
- region (type: tags)
- price (type: select)
Based on Filtering Content on a page - I’ve been able to get the basic functionality working: frontend shows filters based on the content in the backend and all wines show, including pagination.
Usage example:
A page shows the full assortment of wines, including pagination. When a filter is applied using the select boxes the page refreshes and returns all wines that match that criteria.
I run into the following problems on the acceptance environment https://vinura.olafmuller.nl/assortiment
- When a filter is applied (example: Estate A) and the user paginates, the page refreshes, the pagination occurs, however, the filters are reset and the full assortment is shown again. We want to paginate with the query staying intact.
- When no filters are applied and the last page is opened (example: page 6) after applying a filter (example: rood), the page refreshes and opens the last page of wines using the new query; this should be the first page. After paginating again, the first bug occurs again.
- I’d like the filter options to be sorted alphabetically, I’ve been experimenting but filters are objects in an array, and with my limited PHP knowledge, I’ve not been able to get it to work.
The products.php controller:
<?php
return function ($kirby) {
$args = $kirby->route()->arguments();
$products = page('assortiment')->children()->listed();
$estate = $products->pluck('estate', null, true);
$kind = $products->pluck('kind', null, true);
$grape = $products->pluck('grape', null, true);
$region = $products->pluck('region', null, true);
$price = $products->pluck('price', null, true);
$keys = array('estate', 'kind', 'grape', 'region', 'price');
// return all children if nothing is selected
$projects = $products;
// if there is a post request, filter the projects collection
if (r::is('POST') && $data = get()) {
$projects = $products->filter(function ($child) use ($keys, $data) {
// loop through the post request
foreach ($data as $key => $value) {
// only act if the value is not empty and the key is valid
if ($value && in_array($key, $keys)) {
// return false if the child page's category and value don't match
if (!$match = $child->$key() == $value) {
return false;
}
}
}
// otherwise return the child page
return $child;
});
}
$projects = $projects->shuffle()->paginate(3);
$pagination = $projects->pagination();
return compact('projects', 'estate', 'kind', 'grape', 'region', 'price', 'data', 'pagination');
};
The input and output of the products.php template:
<div class="grid">
<aside class="cell cell--1 cell--small-1/3 cell--medium-1/4 is-sticky">
<h2 class="visually-hidden">Filters</h2>
<form id="filters" method="post" class="section section--sticky">
<div class="field">
<label for="estate" class="field__label">Estate</label>
<select id="estate" class="field__input" name="estate" onchange="this.form.submit()">
<option selected value="">Selecteer een estate</option>
<?php foreach($estate as $item) : ?>
<?php if(!$item) continue ?>
<option<?php e(isset($data['estate']) && $data['estate']==$item, ' selected' ) ?> value="<?= $item ?>">
<?= page($item)->title() ?>
</option>
<?php endforeach ?>
</select>
</div>
<div class="field">
<label for="kind" class="field__label">Soort</label>
<select id="kind" class="field__input" name="kind" onchange="this.form.submit()">
<option selected value="">Selecteer een soort</option>
<?php foreach($kind as $item) : ?>
<?php if(!$item) continue ?>
<option<?php e(isset($data['kind']) && $data['kind']==$item, ' selected' ) ?> value="<?= $item ?>">
<?= $item ?>
</option>
<?php endforeach ?>
</select>
</div>
<div class="field">
<label for="grape" class="field__label">Druivenras</label>
<select id="grape" class="field__input" name="grape" onchange="this.form.submit()">
<option selected value="">Selecteer een druivenras</option>
<?php foreach($grape as $item) : ?>
<?php if(!$item) continue ?>
<option<?php e(isset($data['grape']) && $data['grape']==$item, ' selected' ) ?> value="<?= $item ?>">
<?= $item ?>
</option>
<?php endforeach ?>
</select>
</div>
<div class="field">
<label for="region" class="field__label">Regio</label>
<select id="region" class="field__input" name="region" onchange="this.form.submit()">
<option selected value="">Selecteer een regio</option>
<?php foreach($region as $item) : ?>
<?php if(!$item) continue ?>
<option<?php e(isset($data['region']) && $data['region']==$item, ' selected' ) ?> value="<?= $item ?>">
<?= $item ?>
</option>
<?php endforeach ?>
</select>
</div>
<input type="submit" value="Filter" class="button button--large button--primary input-not-mandatory trailer">
</form>
</aside>
<div class="cell cell--1 cell--small-2/3 cell--medium-3/4">
<h2 class="visually-hidden">Overzicht</h2>
<?php if($projects->count()) : ?>
<div class="grid">
<?php foreach($projects as $item) : ?>
<div class="cell cell--1 cell--small-1/2 cell--medium-1/4">
<?php snippet('card-wine', array('item' => $item)) ?>
</div>
<?php endforeach ?>
</div>
<?php else : ?>
<span class="header header--s trailer text-middle is-subjacent">Geen resultaten gevonden die voldoen aan de opgegeven filters.</span>
<?php endif ?>
<?php if($projects->pagination()->hasPages()) : ?>
<nav class="trailer">
<ul class="pagination">
<?php if($pagination->hasPrevPage()) : ?>
<li class="pagination__item"><a class="pagination__link button button--contained" href="<?= $pagination->prevPageURL() ?>">Vorige</a></li>
<?php endif ?>
<?php foreach($pagination->range(10) as $r): ?>
<li class="pagination__item">
<a class="pagination__link button button--contained<?php if($pagination->page() == $r) echo ' is-active' ?>" href="<?= $pagination->pageURL($r) ?>">
<?= $r ?>
</a>
</li>
<?php endforeach ?>
<?php if($pagination->hasNextPage()): ?>
<li class="pagination__item"><a class="pagination__link button button--contained" href="<?= $pagination->nextPageURL() ?>">Volgende</a></li>
<?php endif ?>
</ul>
</nav>
<?php endif ?>
</div>
</div>
```
Edit: I've also added the question about alphabetical sortation of filters.