Wednesday, May 01, 2013

Selecting From a List

Building a Yii Model from an existing database file is very easy.  The tutorial here tells how to build the model and quickly add some CRUD functions.  That is fine if you have a simple flat file, but most useful databases have relationships between their files.  Unfortunately, while the pieces covering how to handle this are easily searchable, you will have to go all over the web to put all the pieces together.  Here are some of the things I wanted to do:
  • populate a drop down box with the available values for the field
  • sometimes I want that drop down box to be loaded based on the value in another field
  • I have a Values List that consists of a header and details, sometimes I want the drop down populated from a given values list. For example I want to display a list of customer statuses that are stored in my values list tables.
  • In some places I want to translate from my foreign key to a description or name.  That is I want to display the customer name instead of the customer id that is stored in my file.
  • In a Grid View, I want to display, sort, and filter by the customer name instead of the customer id stored in my table.
  • I want to be able to select multiple values from a list using check boxes, and have those values configurable from a table (Many to Many relationship)
Lets Get Started.

Placing a Drop Down List

If you have created your CRUD Functions with Gii as directed in the above tutorial, you will have several types of components generated in your views directory.  User input forms, however are consolidated into a single file _form.php.  This will be generated as a CActiveForm widget with Ajax Validation disabled, and populated with a row for each attribute in your model.  These rows will look like this:
<div class="row">
    <?php echo $form->labelEx($model,'country_id'); ?>
    <?php echo $form->textField($model,'country_id',array(
        'size'=>60,'maxlength'=>255)
    ); ?>
    <?php echo $form->error($model,'country_id'); ?>
</div>
This does several things for us.  The labelEx function retrieves the label text from the model.  It is also smart enough to be decorated with an asterisk (*) when entry is required for the field.  The textField inserts a text box control on into the form that is 60 characters wide, and will allow 255 characters to be entered.  And error displays any validation errors that occur for this field when the form is posted.

The problem here is that country_id is a foreign key, and no one will know to enter 3 for the USA, or 7 for Canada.  The solution is to use a drop down list that shows the names of the countries instead of the text box.  To do that we must swap out the following code with a drop down list control.
<?php echo $form->textField($model, 'country_id', array(
    'size'=>60,'maxlength'=>255));?>
Is replaced by:
<?php $list = CHtml::listData(Country::model()->findAll(), 
     'id', 'name'); ?>
<?php echo $form->dropDownList($model, 'country_id', $list,
     array('empty'=>'(Select a Country)')); ?>
First we build the list data, then we load it into a drop down list.  Notice that Country in the listData function is the class name of the Country model. listData builds an array of key=>value pairs which are loaded from the array of models provided in the first parameter.  The second parameter 'id' names the model attribute used to populate the key, and the third parameter names the attribute used to populate the value. There is an optional fourth parameter that is used to provide group names for the list.  dropDownList has four parameters as well.  The first two are the same as the first two parameters for textField.  The third parameter is the array formatted by listData, and the fourth is an array of html options for the drop down. list.  There is a special option which specifies some text for the empty value of the drop down list, and that is 'empty'.  I have set the empty value to '(Select a Country)'.

So that is how to replace a foreign key with a more meaningful drop down box on an entry form.  In future posts I will discuss the remaining points listed above.