transform a model attribute value

In my users table I have an "enabled" field - this can be set to either 1 or 0.

I want to be able to set up in my model a way of transforming this value to a more friendly value i.e. 1="Yes", 0="No".

I can of course do this manually in each view via IF statements but ideally I want this process to be automated in the model so that it’s available anywhere that I call $model->enabled - is this possible?

Model:




public function getIsEnabled()

{

  return $this->enabled ? 'Yes' : 'No';

}



View:




... echo $model->isEnabled; ...



Hi jayRulez,

Thanks, that’s exactly what my colleague has suggested, but I was hoping there was a way of doing this automatically, i.e. without having to call any function?

How can I reference $model->isEnabled in a CDetailView/CGridView?




...,

array(

  'name'  => 'enabled',

  'value' => $model->isEnabled,

),

...



/




...,

array(

  'name'  => 'enabled',

  'value' => '$data->isEnabled',

),

...



If I understand right you have a field "enabled" that is 1 or 0, but in the application you would like it to be "yes" or "no"…

You could use a custom behavior to change the value of enabled, but I’m not sure if that is goot programing practice…

Something like this would work:

myEnable.php - put it for example in protected/components




class myEnable extends CActiveRecordBehavior

{

    /**

     * Convert from Yes/No to 1/0

     */

    public function beforeSave($event)

    {

        $this->Owner->enabled = $this->Owner->enabled == 'Yes' ? '1' : '0';

        return parent::beforeSave($event);

    }


    /**

     * Convert from 1/0 to Yes/No

     */

    public function afterFind($event)

    {

        $this->Owner->enabled = $this->Owner->enabled ? 'Yes' : 'No';

        return parent::afterFind($event);

    }

}



now in the model you attach this behavior like:




    public function behaviors(){

        return array(

            'myEnable'=>array(

                'class'=>'application.components.myEnable',

            ),

        );

    }



Note that with this in place the enabled property now is Yes or No, not anymore 1 or 0… so if you make create/updates rules, you have to check not for integer 1/0 but for text ‘Yes’/‘No’.

Thanks. Actually this also works too:




public function afterFind()

{

	$this->enabled = $this->enabled ? 'Yes' : 'No';

        return parent::afterFind();

}



Sure it works… the behavior variant is just to make it a re-usable component…

NOTE: If you are going to save some data for this model than you should add the beforeSave() method that returns the value to what the database expects.

mdomba’s approach is right on the ball for your situation but not the best thing to do. I take it in your code you’ll want to use the 1/0 representation instead of ‘Yes’/‘No’. eg:


if($model->enabled == 1)

instead of


if($model->enabled == 'Yes')

… Otherwise i don’t see any good reason to store 1/0 in the field when you always use it as ‘Yes’/‘No’.

What I usually do is define class constants to represent states e.g. for your case i’d do




const ENABLED_NO = 0;

const ENABLED_YES = 1;



so a comparison


if($model->enabled == User::ENABLED_NO)

is more meaningful to a reader than


if($model->enabled == 0)

It’s fine to use 1/0 if you’ll be the only one reading the source code and/or if it’s properly documented.

I agree that you should not transform the data within the model, as that will cause issues and dirty up your data model.

I personally only transform 0/1 into no/yes in the actual view, i.e.:




echo ($model->enabled ? 'yes' : 'no');



Funnily enough this actually seems like the best solution! So simple and I don’t have to create any new fucntions or variables!

In fact I would prefer to use this method but not sure how I could do it in a CGridView or CDetailView!

Per the API documentation, you can pass expressions into CGridView (and I assume CDetailView).




$this->widget('zii.widgets.grid.CGridView', array(

    'dataProvider'=>$dataProvider,

    'columns'=>array(

        'title',          // display the 'title' attribute

        'category.name',  // display the 'name' attribute of the 'category' relation

        'content:html',   // display the 'content' attribute as purified HTML

        array(            // display 'create_time' using an expression

            'name'=>'create_time',

            'value'=>'date("M j, Y", $data->create_time)',

        ),

        array(            [b]// display 'author.username' using an expression[/b]

            'name'=>'authorName',

            'value'=>'$data->author->username',

        ),

        array(            // display a column with "view", "update" and "delete" buttons

            'class'=>'CButtonColumn',

        ),

    ),

));



So you could just try something like:




'value'=>'($data->enabled ? "yes" : "no")',



I think this is best solution since you write less code.

Think about this:


'value' => '$data->isEnabled',

instead of


'value' => '$data->enabled ? Yii::t("words", "yes") : Yii::t("words", "no")'

Maybe it’s also better to use “Value” as method suffix so you can also do things like this:




public function getCreatedAtValue()

{

  return date("M j, Y", $this->createdAt);

}




'value'=>'$data->createdAtValue',

Y!!'s method works for me ;)

You could also do a enum(‘Yes’, ‘No’) as SQL Datatype.

Agreed that’s a possibility, though enum is best reserved for a non-boolean response :wink:

But that would be a good alternative