Using numberFormatter in form

hi,

I am using dateFormatter and numberFormatter to format date, time, currency and numbers into users locale.

I have a field that represents a percentage. In the database this is stored as (for example) 0.20 and with the numerFormatter displayed as 20 %.

So far so good. But in a form I can only let the users enter the database value, so a value between 0 and 1. At least, when I use CHtml::activeTextField.

I would like that the percentage value is consistent in the interface.

Someone knows what I should do?

Thanks!

That’s probably because of what’s defined in your rules() method. You can change it to accept an integer between 0 and 100, and override your model’s beforeSave() method to divide the value by 100 before actually saving it to the database.

It’s more that if I use formatPercentage the number gets multiplied by 100 or even 1000 in some cases.

See: http://www.yiiframework.com/doc/api/CNumberFormatter#formatPercentage-detail

If something is for example 20%, I would like that users see 20% everywhere and not suddenly 0.20 in the form.

Hmm. First thing that comes to mind is overriding your model’s __get() method:




public function __get($name) {

 if($name==='someProperty') {

  // return formatted value of $this->someProperty

 }

 return parent::__get($name);

}



and use the beforeSave() method I described earlier to convert the value back before updating the database.

There’s probably a more elegant way to do this though, but I can’t think of it right now.

In cases like this, when displayed/entered type differs from the stored type i usually add a "virtual" attribute to the AR (a public variable that is). In beforeSave()/afterFind() i then convert from the db attribute to the displayed attribute and vice versa respectively. The virtual attribute must be added to safeAttributes() whereas the db attribute should not be safe anymore.


class X extends CActiveRecord {


  public $displayPercentage;


  public function rules() {

    return array(

      array('displayPercentage', 'numerical', 'integerOnly'=>true, 'min'=>0, 'max'=>100),

    );

  }


  public beforeSave() {

    $this->percentage=$this->displayPercentage/100;

  }


  public afterFidn() {

    $this->displayPercentage=$this->percentage*100;

  }


  // don't forget safeAttributes()...


}

If you don’t use a virtual attribute but modify the actual attribute it can mess up things quickly. Think about a float where the decimal point should be converted. If you convert “1.234” to “1,234” you can’t use it for calculations anymore. You also can keep rules() seperate for display values and actual db values.

As this is a very common problem it would be nice to have either a behavior or even some built in support in AR for this. But it should be planned carefully as there are a lot of different conversions that users need (dates, currencies, number formats, …). Need more time to think about it :).

I like your last sentence…

And I like your idea, was it not for the fact that formatPercentage can either be in percent or in promille, so I can never be certain that a value between 0 and 100 is the same as they see outside the form.

I think for the moment I just let them enter a value between 0 and 1…