Advanced filters with numbers for Doctrine. (fr and en)
By Mathieu on Friday, November 13 2009, 13:07 - Symfony - Permalink
Dernièrement j'ai du filtrer les nombres de façon supérieur ou inférieure sur un champ "prix", je me suis alors rendu compte que les filtres de Symfony avec Doctrine ne permettait pas cela, j'ai donc commençait à chercher une solution en me plongeant dans le code de Symfony, sans vraiment chercher sur internet je l'avoue, mais ça fait parfois du bien de voir comment marche l'outil que l'on utilise tous les jours ;) .
Lately I had to filter some numbers on a price field. The aim was to filter by bigger or smaller than the price value. I realized default Doctrine filters doesn't allow to do that, so I started to search a solution by looking at the Symfony code. I admint I don't really look on the internet to find a solution, but sometimes it's good to look inside the tool we use everyday ;).
J'ai donc découvert une façon simple qui est de faire une méthode spéciale pour le champ concerné, qui consiste donc pour un champ "price" de crée la fonction addPriceColumnQuery($query, $field, $value) et qui devra retourner la requête ($query), ainsi de suite... mais ici je ne vous parlerai pas de cette manière de faire, après quelque recherche avec votre ami Google vous trouverez des articles pour ça (enfin si il y a une demande un article peut être possible...)
Le problème pour ceci est qu'il faudrait répéter l'opération pour tous les champs auxquels on voudrait appliquer cette modification, donc nous allons nous pencher sur une autre solution.
I discovered an easy way to do it by creating a specific method for a given field. For exemple for the price field we can create an addPriceColumnQuery($query, $field, $value) which add some selection criteria to the $query and return it. But I won't speak about this manner of doing. You can find some tips about this by searching on Google. (But perhaps I could write a post about it if someone need it.)
If we created a method for a field we need to do the same thing for each field on which we need to apply the change. So let's see another issue.
L'idée serait de renseigner dans notre champ l'opérateur voulu suivi de sa valeur, avec la possibilité d'utiliser les opérateurs suivant: "<", ">", "<=", ">=" et dans le cas où l'on ne saisie pas d'opérateur on utilise "=". On pourrai donc par exemple saisir: >200.
The idea would be to create something to allow us to write in the field the operator with the value. We could use the following operators: "<", ">", "<=", ">=" , and in the case of no operator we will use "=". So for exemple we could fill the field value with: >200.
Donc en fouillant d'avantage on remarque que les champs sont "typés" (voir BaseXXXFormFilter::getFields()) par exemple le champ prix est typé "Number" et que pour filtrer ce champ on fait appel à la fonction addNumberQuery dans sfFormFilterDoctrine.
Donc l'idée est de créer alors un nouveau type: "NumberOperator", de créer la fonction addNumberOperatorQuery qui sera appelé par Symfony lors du filtrage.
''So if we look at BaseXXXFormFilter we can see a getFields() method. This method describe the type for each field of the filter class. For exemple my price field has a "Number" type and so to filter this field symfony will automaticly call the addNumberQuery() method from the sfFormFilterDoctrine class.
So here the idea is to create a new type "NumberOperator" with an addNumberOperatorQuery() mehod and use this to filter the price field.''
Pour ceci on écrit notre fonction dans la classe BaseFormFilterDoctrine et on remercie Doctrine d'avoir pensé à cette classe ;)
To do this we add addNumberOperatorQuery() in BaseFormFilterDoctrine and we say thanks to Doctrine to generate this class ;)
/**
* Project filter form base class.
*/
abstract class BaseFormFilterDoctrine extends sfFormFilterDoctrine
{
const TYPE_NUMBER_OPERATOR = 'NumberOperator';
public function setup()
{
}
public function addNumberOperatorQuery(Doctrine_Query $query, $field, $values)
{
$fieldName = $this->getFieldName($field);
$fields = $this->getFields();
$type = $fields[$field];
if (is_array($values) && isset($values['is_empty']) && $values['is_empty'])
{
$query->addWhere('r.' . $fieldName . ' IS NULL');
}
else if (is_array($values) && isset($values['text']) && '' != $values['text'])
{
if(preg_match('/^(=|<=|>=|<|>)(.+)/', trim($values["text"]), $matches))
{
$query->addWhere('r.' . $fieldName . ' '.$matches[1].' ?', trim($matches[2]));
}
else
{
// Call the default function
if(!method_exists($this, $method = sprintf('add%sQuery', $type)))
{
throw new LogicException(sprintf('Unable to filter for the "%s" type.', $type));
}
$query = $this->$method($query, $field, $values);
}
}
return $query;
}
}
Ensuite dans notre classe de filtre il faut modifier le type de notre champ et changer le validateur, un nouveau le cas échéant pour pouvoir aller avec notre nouvelle fonction crée auparavant.
Then in our filter class we need to change the field's type and also change the validator, a new one in this case just to check that the field value is something like operator+value.
/**
* Product filter form.
*/
class ProductFormFilter extends BaseContactFormFilter
{
public function configure()
{
//...
// we use the new validator
$this->validatorSchema['price'] = new sfValidatorSchemaFilter('text', new myValidatorNumberOperator(array('required' => false)));
//...
}
public function getFields()
{
$fields = parent::getFields();
$fields['price'] = BaseFormFilterDoctrine::TYPE_NUMBER_OPERATOR; // change the field type
return $fields;
}
}
Et notre nouveau validateur:
And so the new validator:
class myValidatorNumberOperator extends sfValidatorNumber
{
protected function configure($options = array(), $messages = array())
{
$this->setMessage('invalid', '"%value%" is not a number "operator" (accepted symbols: "", "=", "<=", ">=", "<", ">").');
}
protected function doClean($value)
{
$compareSymbol = '';
if(preg_match('/^(=|<=|>=|<|>)(.+)/', trim($value), $matches))
{
$compareSymbol = $matches[1];
$numberValue = $matches[2];
}
else
{
$numberValue = $value;
}
try {
$cleanNumber = parent::doClean($numberValue);
}
catch (sfValidatorError $e) {
throw new sfValidatorError($this, 'invalid', array('value' => $value));
}
return $compareSymbol.$cleanNumber;
}
}
Ensuite pour pouvoir filtrer un prix il suffit de renseigner dans notre filtre pour un prix supérieur ou égal à 200 par exemple : ">=200".
Now to filter the price filed you can use values such as: ">=200".
Et en faisant cet article j'ai eu une autre idée faire un filtre qui filtrerai également une valeur suivant deux critères, explications rechercher par exemple un prix supérieur à 100 et inférieur à 300 : "100<x<300" à vos claviers ;)
Traduced by eNk`
Comments
Bon article, c'est dommage à chaque fois de faire tout ça à la main et de le perdre sur le site suivant.
Do you want both quality and price, come here.
[url=http://www.toptoys2trade.com/animal... ]Animal Shaped Rubber Bands[/url],
[url=http://www.toptoys2trade.com/baby-c... ] Baby Carriers [/url],
[url=http://www.toptoys2trade.com/power-... ]power balance[/url],
[url=http://www.toptoys2trade.com/animal... ]Animal Rubber Bands[/url],
[url=http://www.toptoys2trade.com/mosqui... ]mosquito repellent wristband[/url]
[url=http://www.toptoys2trade.com/bakuga... ]new bakugan[/url],
[url=http://www.toptoys2trade.com/p90x-d... ]P90X DVDs[/url],
hung it on the peg reserved for his gear. His gun belt was already slung in place — he hadn’t felt the need to
wear that to the station for a few weeks.<a href="http://www.edhardy-discount.com/">discount ed hardy</a>
There had been no more disturbing disappearances to trouble the
small town of Forks, Washington, no more sightings of the giant, mysterious wolves in the ever-rainy woods. .
I prodded the noodles in silence,<a href="http://www.gobizfashion.com/">puma shoes cheap</a>
guessing that Charlie would get around to talking about whatever was
bothering him in his own time. My dad was not a man of many words, and the effort he had put into trying to
orchestrate a sit-down dinner with me made it clear <a href="http://www.topjerseysworld.com/">discount GHD Hair Straighteners</a>there were an uncharacteristic number of words on his
I glanced at the clock routinely — something I did every few minutes around this time. Less than a half
hour to go now.
hung it on the peg reserved for his gear. His gun belt was already slung in place — he hadn’t felt the need to
wear that to the station for a few weeks.<a href="http://www.edhardy-discount.com/">discount ed hardy</a>
There had been no more disturbing disappearances to trouble the
small town of Forks, Washington, no more sightings of the giant, mysterious wolves in the ever-rainy woods. .
I prodded the noodles in silence,<a href="http://www.gobizfashion.com/">puma shoes cheap</a>
guessing that Charlie would get around to talking about whatever was
bothering him in his own time. <a href="http://www.edhardy-discount.com/">ed hardy clothing</a>
My dad was not a man of many words, and the effort he had put into trying to
orchestrate a sit-down dinner with me made it clear <a href="http://www.topjerseysworld.com/">discount GHD Hair Straighteners</a>there were an uncharacteristic number of words on his
I glanced at the clock routinely — something I did every few minutes around this time. Less than a half
hour to go now.
Edward had a lot of money—I didn't even want to think about how much. Money meant next to nothing
to Edward or the rest of the Cullens. It was just something that accumulated when you had unlimited time
on your hands and a sister <a href="http://www.edhardy-discount.com/">ed hardy bags</a>
who had an uncanny ability to predict trends in the stock market. Edward
didn't seem to understand why I objected to him spending money on me—why it made me
uncomfortable if he took me to an expensive restaurant in Seattle, why he wasn't allowed to buy me a car
that could reach <a href="http://www.gobizfashion.com/">puma shoes cheap</a>
speeds over fifty-five miles an hour, or why I wouldn't let him pay my college tuition (he
was ridiculously enthusiastic about Plan B). Edward thought I was being unnecessarily difficult.
But how could I<a href="http://www.topjerseysworld.com/">discount GHD Hair Straighteners</a> let him give me things when I had nothing to reciprocate with? He, for some
unfathomable reason, wanted to be with me. Anything he gave me on top of that just threw us more out
of balance.
Just a <a href="http://www.chinadesignerwholesale.c...">designerwholesale</a> dream, I told myself. It was only a dream. I took a deep breath, and then jumped again when my alarm went off. The<a href="http://www.giantsupplier.com/">Cheap Men's Brand Clothing</a> little calendar in the corner of the clock's display<a href="http://www.giantsupplier.com/">Fashion Women's Handbags</a> informed me that today was September thirteenth.