Chapter 10.1: View filters
It may not always be appropriate to show all data in a view. You may want to filter data based on some criteria. In this topic we shall learn to create such filters.
Let's go back to our Quotes backend module. We shall create a simple view filter to filter data by statuses.
In Silva
In Silva, this is easily achieved. If you want to create dropdown filter from relations (i.e. foreign keys) in the table, use the $vw->setAutoFilters()
method. Just one line of code and Silva will intelligently insert the filter form in the view and handle the query. However, if you want to filter the grid by a non foreign key field, the method involves writing a bit of code. I will use the Q-method as described in the documentation.
First of all define a filter form and attach it to the view. The method prepares a data array of filter values which will be returned to the calling view.
protected function getFilterForm(Silva_View $vw)
{
$filters = array(
'status' => isset($_GET['status']) ? $_GET['status'] : '',
);
$filterForm = Silva_View::getFilterForm(array(
'status' => array('select', array(
'label' => 'Filter by quote status',
'multiOptions' => array('' => '[ All statuses ]') + array(
'new' => 'New',
'sent' => 'Sent',
'viewed' => 'Viewed',
),
'value' => $filters['status'],
'onchange' => Silva_View::JS_SUBMIT_FORM,
)),
));
// attach the filter form to the view.
$vw->setFilterForm($filterForm);
return $filters;
}
Then, in the view, you prepare a custom query based on the filter selection and pass this query object to the getGrid
method.
public function onQuoteGridInit(Silva_View $vw)
{
$filters = $this->getFilterForm($vw);
// we need to define our query when we create custom filters
// since Silva doesn't know which custom filters you have defined.
$q = QuoteQuery::create();
if ($filters['status']) {
$q->filterByStatus($filters['status']);
}
// buttons in Flexigrid are the same things as "actions" in ModelView.
$buttons = array(
// Code removed to focus on relevant portions
...
);
// We inform Silva to use our customized query ($q) instead of automatically creating one.
$grid = $vw->getGrid($buttons, null, $q);
...
}
In ModelView
Preparing filters in ModelView is a tricky affair. If you want filters in more than one nested views, I recommend you to go for Silva. You would write much less code in Silva than ModelView to achieve the same effect.
In the Quotes module, define a filter form as follows:
protected function getFilterForm(array $filters)
{
$form = new Curry_Form(array(
'action' => (string) url('', $_GET),
'enctype' => Zend_Form::ENCTYPE_URLENCODED,
'method' => 'post',
'class' => 'filters-form',
'elements' => array(
'status' => array('select', array(
'label' => 'Filter by quote status',
'multiOptions' => array('' => '[ All statuses ]') + array(
'new' => 'New',
'sent' => 'Sent',
'viewed' => 'Viewed',
),
'value' => $filters['status'],
)),
'do_filter' => array('submit', array('label' => 'Filter')),
),
));
return $form;
}
Notice that we don't use the 'GET' request method in the filter form as we do with Silva. We have to use the POST method or else we will have problems with data hydration when the grid does a callback to fetch data in JSON format. Next, move the list construction code to another method where you would construct a custom query based on the filter selection:
protected function getMainList($filters = null)
{
// Code removed to focus on relevant portions
...
// custom query to handle filters
$q = QuoteQuery::create();
// handle filters
if (isset($filters['status']) && $filters['status'] != '') {
$q->filterByStatus( $filters['status']);
}
$list = new Curry_ModelView_List($q, array(
// Code removed to focus on relevant portions
...
));
return $list;
}
Finally, modify the showMain
method as follows:
public function showMain()
{
$filters = array(
'status' => isset($_GET['status']) ? $_GET['status'] : '',
);
$fltrForm = $this->getFilterForm($filters);
if (isPost()) {
if ($fltrForm->isValid($_POST)) {
$filters = $fltrForm->getValues(true);
unset($filters['csrf']);
$namespace = new Zend_Session_Namespace(__METHOD__);
foreach ($filters as $key => $val) {
$namespace->{$key} = $val;
}
} elseif (isset($_GET['action']) && $_GET['action'] == 'json') {
$namespace = new Zend_Session_Namespace(__METHOD__);
foreach ($filters as $key => $val) {
if (isset($namespace->{$key})) {
$filters[$key] = $namespace->{$key};
}
}
}
} elseif (Zend_Session::namespaceIsset(__METHOD__)) {
Zend_Session::namespaceUnset(__METHOD__);
}
if (!isset($_GET['action'])) {
// show filters only on main view and not on child views.
$this->addMainContent($fltrForm);
}
$list = $this->getMainList($filters);
$list->show($this);
}
You will have to repeat the above technique for each nested view having a view filter.