Basic Model Edits in Yii

November 10, 2009
The Yii Book If you like my writing on the Yii framework, you'll love "The Yii Book"!
This entry is part 6 of 8 in the series Learning the Yii Framework

This is the sixth post in my series on Yii, my favorite PHP framework. [intlink id=”473″ type=”post”]In the first[/intlink], I show how to download and test the framework itself. [intlink id=”563″ type=”post”]In the second[/intlink], I show how to create a basic Web application.[intlink id=”583″ type=”post”] In the third[/intlink], I walk through some configuration options. [intlink id=”607″ type=”post”]In the fourth[/intlink], I explain my database design, and how you should define it with Yii in mind. [intlink id=”622″ type=”post”]In the fifth[/intlink], I show how to use Yii’s Gii tool to create Models, Views, and Controllers in your Yii application. In this post, I walk through some of the basic edits you’ll likely make to a Model after it’s been created by Yii’s Gii tool. In doing so, you’ll also get a general introduction to the Yii Model as a whole. For some of the code, I’ll be using the employees-departments example I’ve been developing throughout these posts. You may want to reread earlier posts to get a handle on those.

(Note: In October 2010, I’ve updated this entire series to reflect changes in Yii since this series was written, and to take into account feedback provided through the comments. Some outdated material will be crossed out, but left in to reflect how things have changed since the series was begun in June 2009.)

The Model represents the data used by an application. Normally the data comes from a database, but it could also be from a form submission (to be emailed, not stored, like the ContactForm Model in the base Yii application). In Yii, a Model based upon a database table is defined as a class that extends (i.e., inherits from) Active Record. Active Record is a common and popular design, also used by the Ruby on Rails framework. This means that most of the Model’s functionality isn’t defined in your Model, but rather in the parent Active Record class. So when looking at your Model code, don’t be surprised that you won’t find methods for creating and updating records (i.e., saving the Model) or whatever, and that’s all inherited. Since that functionality is defined already for you, the goal of your Model should be to extend and tweak that functionality as needed.

Within a Model, there are certain Yii-specific methods (class functions) that you’ll commonly use. Some of these will be created when you use Yii’s Gii tool to generate the Model, others can be added whenever. I want to focus on those Yii-specific methods here, as they’ll be common to most Models. (In later posts, I’ll write up examples of custom methods that might be added to Models.)

One of the most important methods is rules(), which lists the rules by which the Model data must abide. Much of your application’s security and reliability stems from this method. In fact, this one method represents one of the key benefits of using a framework: data validation. Whether a new record is being created or an existing one is updated, you won’t have to write, replicate, and test the data validation routines: Yii will do them for you. As a comment in the Model indicates, you only establish rules for fields (i.e., Model attributes) whose data may be provided by users. You wouldn’t, for example, declare a rule for the Employee id field, which is the MySQL-generated primary key.

The rules() method, like most of the Yii methods, returns an array of data:

public function rules()
{
    return array(/* actual rules */);
}

For the actual rules, the Yii documentation covers them in full (see this and this), but I’ll highlight the main ones. As you’ll see, each rule is written so that it returns an array.

The first, most obvious, restriction is to indicate that a field is required. Just use a comma-separated string of field names as the first returned value and the word required as the second:

array('name, email, subject, body', 'required'),

You can also specify that a value must be a number or, more specifically, an integer. This syntax is slightly different. Here, I indicate that the ext field must be an integer:

array('ext', 'numerical', 'integerOnly'=>true),

For strings, you can restrict the length to a maximum value:

array('name','length','max'=>40),

Or a minimum value:

array('name','length','min'=>6),

Or both:

array('name','length','min'=>6, 'max'=>40),

Another useful validation routine is to check that a string is an email address. I do that here, to a userEmail field:

array('userEmail', 'email'),

To indicate that a string needs to be a URL, use:

array('link', 'url'),

Another useful rule is for comparisons, like when a user registers and you have to make sure that the confirmed password matches the password:

array('password1', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),

There is also the “safe” rule. This rule is used to provide access to data that isn’t otherwise validated. For example, an email address is already considered to be “safe” because it must abide by the email rule, but the Employee Model has the leaveDate field which won’t have any rules applied to it (in part, because there are no date-specific rules and also because the field can be null). To be able to refer to that value, it must be declared as safe:

array('leaveDate', 'safe'),

If there are multiple fields to make safe, just separate them with commas.

There’s also a rule to indicate which fields should be safe to search on. By default, every rule is made safe in a search, but you may want to change that behavior by removing fields from the list:

array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),

So, using this information, the complete rules() method for my Employee Model is:

public function rules()
{
    return array(
        array('departmentId, firstName, lastName, email, hireDate', 'required'),
        array('departmentId, ext', 'numerical', 'integerOnly'=>true),
        array('firstName', 'length', 'max'=>20),
        array('lastName', 'length', 'max'=>40),
        array('email', 'length', 'max'=>60),
        array('email', 'email'),
        array('leaveDate', 'safe'),
        array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
 );
}

Moving on, another key Model method is relations(), which indicates the relationship between Models. If your database is designed properly, this method will already be properly filled out, again thanks to Gii. Here’s how the relations() method in the Employee Model looks:

public function relations()
{
    return array('department' => array(self::BELONGS_TO, 'Department', 'departmentId') );
}

The relation is given a name, here department. The relation indicates that the departmentId column in the Employee Model (i.e., this one) belongs to the Department Model. Here’s how this will come into play: When loading the information for an employee, you can also load any of its relations. By doing so, references to department will equate to the Department Model record that is the employee’s department. So if the $model object represents the Employee being viewed, then $model->department->name would be the name of the department that the employee is associated with.

In the Department Model, this relation is defined:

public function relations()
{
    return array('employees' => array(self::HAS_MANY, 'Employee', 'departmentId') );
}

So if $model represents a specific Department being viewed, then $model->employees is an array of Employee objects, each of which representing one Employee in that department.

The relations between Models is a key player in complex MVC sites. Through properly defined relations, associated content can be retrieved. You’ll learn more about this in the next couple of posts.

Moving on, a more trivial, but still nice, method is attributeLabels(). This method returns an associative array of fields and the labels to use for those fields in forms, error messages, and so forth. The Yii framework does a great job of making these conversations automatically, like firstName becoming First Name and departmentId becoming just Department. But you may want to still customize these. For the Employee Model, here’s what I have:

public function attributeLabels()
{
    return array(
        'id' => 'Employee ID',
        'departmentId' => 'Department',
        'firstName' => 'First Name',
        'lastName' => 'Last Name',
        'email' => 'Email',
        'ext' => 'Ext',
        'hireDate' => 'Hire Date',
        'leaveDate' => 'Leave Date',
    );
}

So there’s a little bit of customizing you’ll want to do to your Models to start off. As I said, you’ll also add your own methods later on. And there’s some other Yii-specific methods that I do use often, like beforeSave(), which is code to be run before a Model is saved, and beforeValidate(), which is executed before validation takes place. This last method is useful if there’s some data manipulation you want to do behind the scenes before the Model is run through all the validation requirements. And there’s the search() method, which I’ll have to address in a separate post. But, the absolute most important Model functions are rules() and relations().