Handling Checkboxes in Yii with non-Boolean Values

July 25, 2010
The Yii Book If you like my writing on the Yii framework, you'll love "The Yii Book"!

On a recent Yii-based project, managing one of the Models required a whole slew of checkboxes to indicate that yes, the quality does apply, or no, it does not. In this case, the value being stored in the database for each attribute was a single letter: Y/N. However Yii, when showing the form to update an item, needs the checkbox value to be a Boolean, in order to properly pre-check the box. Changing the database wasn’t an option in this case, so I had to figure out a good conversion process. In this post, I’ll tell you exactly how I solved this issue.As I said, for the item in question, the database table had multiple columns, each of which only stored either Y or N. As an example, think of a users table with multiple opt-in options: newsletters, offers, etc. When the user creates and updates their account, presenting these as checkboxes is appropriate. But for the administrator, it’d probably be best to view user preferences by just seeing Y/N for each option (although the code I’m going to present wouldn’t be significantly different if you stored 1/0).

The protected/views/modelName/_form.php file defines the form that’s used to both create and update an individual record. In it, checkboxes are created using:

<?php echo $form->checkBox($model,'attribute'); ?>

I can pass an array of options to the checkbox() method in order to tweak how it behaves. In my example, I want to set the values assigned to the Model in both the checked and unchecked states. Here’s how you do that:

<?php echo $form->checkBox($model,'attribute',array('value' => 'Y', 'uncheckValue'=>'N')); ?>

The value option literally creates the value=”Y” code in the checkbox HTML. The uncheckValue option is something that Yii adds that allows you to indicate what the value should be if the box is not checked. It’s a nice addition, saving you from having to add code in the Model to define the value when the box isn’t checked.

So now my Model will receive, and the database will store, either Y or N, depending upon whether the box is checked or not. The next hurdle comes for properly handling updates. An update uses this same form, but the above code alone will not automatically check the box should the database store the value Y for the given attribute. My solution, then, was to convert Y to true and N to false when retrieving the Model item, but only on an update. In the other situations where a Model record might be retrieved and used, the Y/N values are preferable.

In the Controller for this Model, the update action is where I want to start my changes. That method first loads an individual Model using the Controller’s loadModel() method:

public function actionUpdate() {
    $model=$this->loadModel();

After this point, I want to convert all instances of Y/N to true/false, so I invoke a Model method (I’ll define next) to perform that task:

$model->convertToBooleans();

The convertToBooleans() method is defined in the Model. It first defines an array of all attributes that need the conversion:

public function convertToBooleans() {
    $attributes = array('newsletter', 'offers', ...);

This list of strings must exactly match the names of the corresponding Model attributes. From here, there are a couple of ways to proceed. One obvious route is to loop through the array using a foreach. Within the foreach, the ternary operator is used to assign to the Model’s attribute a Boolean based upon its current value:

foreach ($attributes as $attr) {
    $this->$attr = ($this->$attr == 'Y') ? true : false;
}

So that’s all you need to do to make this work. But if you’re like me and you prefer to make your methods as atomic as possible, you could create a separate Model method that performs the specific conversion:

public function convertAttributeToBoolean($attr) {
    $this->$attr = ($this->$attr == 'Y') ? true : false;
}

In this case, the convertToBooleans() method needs to call this other method for each item in the array. The array_map() PHP function can apply a function to every element in an array:

array_map('someFunction', $someArray);

To use an object method, use this syntax:

array_map(array($object, 'methodName'), $someArray);

So the convertToBooleans() method could do this:

array_map(array($this, 'convertAttributeToBoolean'), $attributes);

And that’s what you need to do to make form checkboxes work with non-Boolean database values (MySQL does support storing of Booleans, though). Remember that you first need to update the _form.php script, which will be used for both adding and updating records. Then you only need to convert the Y/N values to Booleans when you go to update a record.

I hope this helps and thanks for reading. Let me know if you have any questions or comments. Thanks, Larry!