Once upon a time I needed to make custom filters for my admin generator...
Example case
Let's analyse a simple study case. Suppose we are running an online shop. The schema includes a product table which has two properties (among all other properties):
quantity
- the number of products we have on our storequantity_alarm
- the shop manager should get alarmed when the number of products go below this
For example, we have such row in the product table:
name: bicycle
quantity: 1294
quantity_alarm: 100
This means we're having 1294 bikes on the store at the moment and the shop manager gets alarmed when the number of bikes reaches 100.Simply, the manager of the shop wants to find the products he's running out of. He just wants to have something like a checkbox in his product admin generator filters. When he runs the filter with alarming attribute checked, he wants to see only those products which number is below the quantity_alarm.
Programming part
Configure the filters section of your admin generator:
filter:
display: [ alarming ]
fields:
alarming:
label: alarming quantity
help: products needing supplies
Modify ProductFormFilter
class. Let's begin with the configure
method:
public function configure()
{
// ...
$this->manageFieldAlarming();
}
protected function manageFieldAlarming()
{
$this->widgetSchema ['alarming'] =
new sfWidgetFormChoice(array(
'choices' => array(
'' => 'yes or no',
1 => 'yes',
0 => 'no'
)));
$this->validatorSchema ['alarming'] =
new sfValidatorPass();
}
Update the getFields
method:
public function getFields()
{
$fields = parent::getFields();
$fields['alarming'] = 'alarming';
return $fields;
}
Add the following method which will handle the database stuff:
public function addAlarmingColumnQuery($query, $field, $value)
{
Doctrine::getTable('Product')
->applyAlarmingFilter($query, $value);
}
Now we need to create the table class method that we just called in the code above. Go to ProductTable
model class and add the following:
/**
* Applies the alarming attribute to a given query retrieving products.
*
* @param Doctrine_Query $query - query to have alarming attribute applied.
* @param Integer $value - alarming?
*/
static public function applyAlarmingFilter($query, $value)
{
$rootAlias = $query->getRootAlias();
switch ($value)
{
case '0':
$query->where($rootAlias.'.quantity > '.$rootAlias.'.quantity_alarm');
break;
case '1':
$query->where($rootAlias.'.quantity <= '\.$rootAlias.'.quantity_alarm');
break;
}
return $query;
}
The whole thing is complete!
The code could be written in many different ways, of course. The sfValidatorPass
is used to pass unchanged filter values. There are three distinct values possible: empty string for 'yes or no', '0' for 'no' and '1' for 'yes'. If empty string is passed, we ignore it. If '0' or '1' is passed, we check it inside the switch statement.
If you're wondering about sfValidatorBoolean, it can't be used, since there are 3 options (yes, no, yes or no) and the boolean validator can handle only two values (but it can be used in other custom filters with no problems).
Notes
The above article is based on a magnificent symfony forum post by dlepage. It has been tested on symfony 1.4 but should work also with versions 1.2 and 1.3.
Wow, that's it !!!
ReplyDeleteThis helped me make my admin-filters so much more usable, the admin-users will kiss my feet from now on ...
Thank You very much
Great Job !!!
ReplyDeleteThat was the final solution to my implementation of a filter of kind "Group is managed by" with choices "Mister_A, Mister_B, Mister_C, Unmanaged". In that case, I didn't want to have all the users shown, but only those ones, that are currently managing a group. Filtering the "Unmanaged" was an unsolved problem too, until I found that example/tutorial.
Thanks a lot, You made my day shiny and bright.
Thank you :)
ReplyDeleteI must ask makers of sf, why do we have to write so much bs to add 1 field to filter form?
It's so frustrating! I had to change one field from TEXT type to SELECT, and had to write all this? Come on!
Too much magic is done behind the scene, undocumented.
Thanks again! :)
you might have used the sfValidatorChoice validator as follows:
ReplyDeletenew sfValidatorChoice(array('required' => false, 'choices' => array('', 1, 0)))
2012 an still this post is very useful :) dzieki!
ReplyDeleteIt's 2015 now and some people still have to deal with legacy code written in sf1 ;)
Deletegreat article !
ReplyDelete