The purpose of this bundle is to provide a way to get some filters that stay in session, to be able to use them when displaying a list of items. It also supports sorting.
Run
`composer require pugx/filter-bundleNo configuration is required.
Inject the provided service in your controller and use it with a form.
Your form should use QUERY (see below) or GET as method, use some fields that make sense on your list of items,
and not use CSRF protection.
The first step is saving the filter with a name (if the form is submitted). Then, you can get
a key/value array in $filter->filter('foo'), where "foo" is the name you provided above.
Using such an array to retrieve the filtered value is up to you: this bundle makes no assumptions about your domain and doesn't do magic.
Here is an example:
<?php
class FooController extends AbstractController
{
public function itemList(Repository $repository, Filter $filter): Response
{
if ($filter->saveFilter(Form\FooFilterType::class, 'foo')) {
return $this->redirectToRoute('foo_item_list');
}
// this is just an example: please implement your own method
$foos = $repository->getList($filter->filter('foo'));
return $this->render('foo/item_list.html.twig', [
'form' => $filter->getFormView('foo'),
'foos' => $foos,
]);
}
}{# this will display your form. Use '_pugx_filter_b4.html.twig' or '_pugx_filter_b5.html.twig' for Bootstrap 4 / 5 #}
{{ include('_pugx_filter.html.twig' with {name: 'foo'}) }}
{% for foo in foos %}
{# here you can display your list of filtered items, as long as you did your homework #}
{% endfor %}<?php
class FooFilterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', Type\TextType::class, ['required' => false])
->add('email', Type\TextType::class, ['required' => false])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'csrf_protection' => false,
'method' => 'GET',
]);
}
}The QUERY method
is the preferred one: it's safe like GET, but filter values travel in the request body
instead of the URL. GET is still supported and stays the default of AbstractFilterType.
To use it, set QUERY as method of your form:
$resolver->setDefaults([
'csrf_protection' => false,
'method' => 'QUERY',
]);No changes are needed in your controller, except for the accepted methods.
Browsers can't submit a form with QUERY method, so Symfony renders it as POST with
a hidden _method field. The bundle accepts both:
- a real
QUERYrequest, sent by the provided JavaScript helper (see below) - the
POSTfallback, when JavaScript is not available
You can use the provided Twig extension for column sorting functionality.
Example of template:
{# Use '_pugx_sort_b4.html.twig' or '_pugx_sort_b5.html.twig' for Bootstrap 4 / 5 #}
{% from '_pugx_sort.html.twig' import sort -%}
{% block body %}
{{ include('_pugx_filter.html.twig' with {name: 'foo'}) }}
<table class="table">
<thead>
<tr>
<th scope="col">Name {{ sort('my_sorting_route', 'foo', 'name') }} </th>
</tr>
</thead>
<tbody>
{% for foo in foos %}
{# same as above... #}
{% endfor %}
</tbody>
</table>
{% endblock %}You need to provide a route/action to perform sorting, using $filter->sort('foo', $field, $direction).
Then, you'll find an additional value inside your filter array, like this:
$filters = [
'_sort' => [
'field' => 'name',
'direction' => 'ASC',
]
];You can use this value to perform your sorting (again, this is up to you, and it depends on your domain logic).
Translations are available (for now, only for English/French/Italian).
The translations should be automatically discovered.
A jQuery helper is provided to enhance UX. You can use it by requiring the following line in your package.json file:
{
"dependencies": {
"@pugx/filter-bundle": "file:vendor/pugx/filter-bundle/assets"
}
}Then you can do something like the following:
// assets/js/app.js
import '@pugx/filter-bundle/js/filter';
$(document).ready(function () {
'use strict';
if (jQuery().pugxFilter) {
$('#filter').pugxFilter();
}
});The basic results will be that the icon in the toggle button will be toggled along,
and the arrow will point to the right when filters are shown (and back to the bottom when
the filters are collapsed).
If the form uses the QUERY method, it will be submitted with a real QUERY request.
When the filter is saved, the current page is reloaded: the redirect destination of your
controller is ignored, since JavaScript can't read it. If you redirect elsewhere (e.g. dropping
the page number), don't use the helper on that form: the POST fallback follows the redirect.
If the QUERY request fails, the form is submitted again with the POST fallback.
You can pass an option object to pugxFilter function.
Currently supported options are:
callbackHidea callback to be used when filters are collapsedcallbackShowa callback to be used when filters are shown
If you prefer vanilla JavaScript with Bootstrap, you can use the following snippet:
import Collapse from "bootstrap/js/src/collapse";
let collapses = document.querySelectorAll("[data-bs-toggle='collapse']");
Array.from(collapses, (collapse) => {
new Collapse(collapse);
let filter = document.getElementById("filter");
if (filter) {
filter.addEventListener("hide.bs.collapse", () => {
let icon = collapse.querySelector("button.filter svg, button.filter i");
icon && icon.classList.remove("fa-angle-right");
icon && icon.classList.add("fa-angle-down");
});
filter.addEventListener("show.bs.collapse", () => {
let icon = collapse.querySelector("button.filter svg, button.filter i");
icon && icon.classList.remove("fa-angle-down");
icon && icon.classList.add("fa-angle-right");
});
}
});The following helper functions are available as functions in Twig templates:
filter_has: tells if a filter is enabled (e.g. if a session for the filter exists)filter_is: tells if a filter field is selectedfilter_is_not: tells if a filter field is not selected
