Basic Model Edits in Yii
This is the sixth post in my series on Yii, my favorite PHP framework. In the first, I show how to download and test the framework itself. In the second, I show how to create a basic Web application. In the third, I walk through some configuration options. In the fourth, I explain my database design, and how you should define it with Yii in mind. In the fifth, I show how to use Yii’s command-line tools 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 command-line tools. 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.
So 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 command-line tools 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.
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 the 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'),
So, using this information, the complete rules() method for my Employee Model is:
public function rules()
{
return array(
array('firstName','length','max'=>20),
array('lastName','length','max'=>40),
array('firstName, lastName, hireDate', 'required'),
array('ext', 'numerical', 'integerOnly'=>true)
);
}
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 Yii’s command-line tools. Here’s how the relations() method in the Department Model looks:
public function relations()
{
return array(
'deptHead' => array(self::BELONGS_TO, 'Employee', 'deptHeadId'),
);
}
The relation is given a name, here deptHead. The relation indicates that the deptHeadId column in the Department Model (i.e., this one) belongs to the Employee Model. Here’s how this will come into play: When loading the information for a department, you can also load any of its relations. By doing so, references to deptHead will equate to the Employee Model record that is the department head. So if $dept is the loaded Model, then $dept->deptHead->firstName would be the first name of the department’s head.
Edit for Yii 1.1: This version of the framework did a slightly better job recognizing the two relationships between Employee and Department. Specifically, it picked up both relationships: that multiple employees are in a single department and that one employee is the department head. Here is the newer version of the relations() method in the Department Model that was auto-generated:
public function relations()
{
return array(
'deptHead' => array(self::BELONGS_TO, 'Employee', 'deptHeadId'),
'employees' => array(self::HAS_MANY, 'Employee', 'departmentId'),
);
}
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 deptHeadId becoming just Dept Head. 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',
'ext' => 'Extension',
'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 safeAttributes() method, which is part of your security plan, but merits a discussion of its own. See the Yii documentation, or perhaps later posts here, for more.

Hi:
This is a great tutorial, thank you. Regarding the relations() method, in my case Yii 1.0.10 generated an additional relationship from what you describe:
Department model
—————-
‘employees’=>array(self::HAS_MANY,’Employee’,'departmentId’)
Employee model
————–
‘departments’=>array(self::HAS_MANY,’Department’,'deptHeadId’)
Are these intended to establish the one-to-many/many-to-one relationships defined in the tables? Thanks.
Jose
Comment by Jose Reyes — November 20, 2009 @ 9:39 am
Hello Jose. Thanks for your comments and my apologies for not replying sooner. Yes, those are to establish the relationships defined in the tables. The indication in the Department Model that each department has many employees is correct. However, the Employee relation is technically one-to-one, as each Employee might show up once in Department (as the department head) and each Department has only one Employee as a department head. This case, with these two Models, is a bit tricky as there are two relationships between the Models, which is less common (one relationship is the employees in a department, the other is the department head for the department). At the end of the day, though, how relations are defined in your Models really just impacts how you access related Models in Controllers and Views.
Comment by Larry — December 7, 2009 @ 9:30 pm
Thanks for great tutorials on yii. I have been mostly developing on java but want to try various rapid development platforms/languages and it looks like yii/php is one of them.
One question I have on model editing is what if my model changes over time? Does that mean I will have to hand edit the model to add additional attributes? Because if I regenerated them, it will overwrite my changes? Or you think it is better to have an extended class that has the overridden behavior so that I can always regenerate my models when schema changes happen.
Thanks.
Comment by Narinder — December 19, 2009 @ 12:33 pm
Thanks for the feedback. This is a good question, by the way. When you’re absolutely new to PHP and YII, if you change the Model, you may want to use the command-line tools to have Yii auto-generate a new Model definition (like in a dummy version of the application). Over time, as you get comfortable with both PHP and Yii, you’ll have no problems just hand-editing the Model class file should/when you make Model changes later. I don’t think there’s a need to create a class to extend.
Comment by Larry — December 19, 2009 @ 8:51 pm
Hi Larry,
I would like to add a varchar field to my database
Do you have any pointers on what I need to do when updating a model file to account for this addition?
I don’t know if I can use the auto crud tool because the model file has already been updated.
Best,
Nima
Comment by Nima — December 29, 2009 @ 3:29 am
If you haven’t modified the originally generated Model file, you can use the command-line tools to regenerate it. Otherwise you just need to make a couple of minor edits to the Model. You’ll also likely need to edit your Views, too.
Comment by Larry — December 29, 2009 @ 3:09 pm
Wow, amazing tutorial. I had been putting off trying Yii after looking at other frameworks like Cake and CodeIgniter and having PHP nausea before starting, but I was so amazed and how easy this was to setup. I was convinced that Django was the most user friendly model but this seems at first try surprisingly easier. I think the days of hand coding whole PHP applications (and many headaches) will be a thing of the past
Thanks for great tutorial!
Comment by JustinJools — January 10, 2010 @ 7:07 pm
Thanks for the nice words. I really appreciate it!
Comment by Larry — January 11, 2010 @ 5:41 pm
Hi Larry,
again I am having one question. Is there any IDE which can integrate yii. I mean instead of going on command-prompt / terminal, it will be better if we can use some wizard type of thing where we will fill up some information and click next->next and everything will be done. And later on that IDE will allow a good text editor with intellisense to edit the code.
Now I really like this framework. It’s time-saver and fast compare to code-ignitor that I was using from a long time. I am still reading your 8 post tutorial, now only 2 posts are remain to read .. I will soon finish both
Comment by Master — February 28, 2010 @ 10:49 am
Good question. Unfortunately I don’t know of an IDE with built-in support for Yii, but that doesn’t mean one doesn’t exist.
Comment by Larry — February 28, 2010 @ 4:44 pm
I think you’re missing the first letter here mate…
This is a really great tutorial, thanks again!!! =)
Comment by walrus — March 14, 2010 @ 5:13 pm
Thanks for the T and for the nice words!
Comment by Larry — March 14, 2010 @ 6:28 pm
Ahh this is turning out to be a fine tutorial. In the above page, you show the final relationship given the above info but you leave out the line below …
array(‘password1′, ‘compare’, ‘compareAttribute’=>’password2′, ‘on’=>’register’),
I am not clear if this is an omission or do I need to put the above code somewhere else than the relations function?
Also I note the poster asking if there is an IDE that supports Yii and I had just watched a video showing a fellow who used NetBeans ide to create a project from existing YII auto-generated code. Unless I’m missing something here, it appears that NetBeans was well up to the job as he used it throughout the video. I also remember reading recently that Netbeans has MVC support added in.
Comment by Garry Freemyer — April 29, 2010 @ 3:23 pm
Thanks for the feedback and the comments. The password comparison would be a rule, not a relation, and it’s just an example. It wouldn’t be used in the employees-departments example I’m developing.
Thanks for the heads-up on NetBeans. It’s a top-notch IDE (and it’s free!), so it wouldn’t surprise me if it supports Yii.
Comment by Larry — May 1, 2010 @ 5:47 pm
Hi Larry,
I have a table by name ‘Service Status’ in my database. How can i generate the necessary model and curd files from the command line.
I am able to generate the model files using ‘model *’ but unable to generate the ‘CURD’ related files.
Thanks & Regards,
Prakash
Comment by prakash — May 2, 2010 @ 9:20 pm
You should read my post about using CRUD.
Comment by Larry — May 3, 2010 @ 8:05 am
Enjoying your series of Yii posts.
I’ve managed to do as instructed so far (apart from being able to add the correct comment to the employee table).
I got to see the list pages for employee and department and I managed to add a department record but I get a CDbException when trying to add an employee record. Looking at the debug contents I see a warning:
Failed to set unsafe attribute “hireDate”.
in
C:\Web\WebServer\Apache22\htdocs\yiitest\protected\controllers\EmployeeController.php
(75)
in C:\Web\WebServer\Apache22\htdocs\yiitest\index.php (13)
Then some errors:
exception ‘CDbException’ with message ‘CDbCommand failed to execute the SQL
statement: SQLSTATE[HY000]: General error: 1292 Incorrect datetime value:
” for column ‘leaveDate’ at row 1′
I’ve checked the table in phpMyAdmin and the leaveDate column is set to allow null values so I’m not sure what the problem is. Any pointers would be appreciated but I appreciate you probably didn’t write these articles with a view to holding everyone’s hand at each step
p.s. I’m a bit confused as to why hireDate is set to be a required field – I thought the idea was that if it was left blank then the current timestamp would be used. I have tried making mine accept null values for that field also, otherwise is an admin expected to know and enter the current timestamp?
Comment by Jon — July 16, 2010 @ 11:43 am
Actually I have managed to get an employee record inserted now. I had to enter a leaving date!. Doesn’t seem quite right but at least it’s in. The question about the hireDate being a required field still applies though – what is the admin supposed to enter in here. In my successful record creation I typed in a random 11 digit number for hireDate to simulate some kind of timestamp but the created record actually used the current timestamp rather than mine anyway
Comment by Jon — July 16, 2010 @ 11:48 am
Thanks for the nice words. As for your dates, you need to make sure your Model definition doesn’t demand that they populated. That’s the hireDate issue. For the leaveDate, I expect you haven’t told MySQL to use the current timestamp for that field if the supplied value is NULL.
Comment by Larry — July 17, 2010 @ 10:20 am
See my answers to your previous questions.
Comment by Larry — July 17, 2010 @ 10:20 am
Thanks for your replies Larry. I’m gradually seeing the bigger picture with regards to yii and what we’re doing here
(It takes me some time!).
One more question – where there is a relationship between tables (e.g. An employee belongs to a department) would you not expect the department field (when creating / editing an employee record) to be a drop down listing the available departments rather than an empty text field meaning you would have to manually enter the id of the department (which you wouldn’t know without going to the departments index and finding the id of the correct department there or some such)
many thanks. I do like the way you explain things
Comment by Jon — July 21, 2010 @ 9:22 am
Sorted now. I found the answer in part 7 of your tutorial thanks
Comment by Jon — July 21, 2010 @ 9:53 am
You’re quite welcome. Thanks for the nice words.
Comment by Larry — July 22, 2010 @ 8:47 am
Gotta admit I’m still struggling with this hireDate field.
Here is a screenshot of my employee table in PhpMyAdmin
http://www.online-presence.com/yii_db.jpg
and in my employee model these are the rules taht were auto-generated by the yii command line tools:
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array(‘departmentId, firstName, lastName, email’, ‘required’),
array(‘departmentId, ext’, ‘numerical’, ‘integerOnly’=>true),
array(‘firstName’, ‘length’, ‘max’=>20),
array(‘lastName’, ‘length’, ‘max’=>40),
array(‘email’, ‘length’, ‘max’=>60),
array(‘leaveDate’, ‘safe’),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array(‘id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate’, ‘safe’, ‘on’=>’search’),
);
}
I noticed hireDate was not in there and tried adding a safe rule for hireDate but still get critical errors when trying to add a record.
With your example should I be able to leave Hire date blank when adding an employee record?
Comment by Jon — July 22, 2010 @ 9:05 am
That really depends upon how the database table is defined. I don’t know what version of MySQL you’re using, but you may have to add a secondary attribute to the hireDate column indicating that the CURRENT_TIMESTAMP should be used on INSERT. Or you can add code to your Model to automatically provide that information. I have that in this series somewhere, if not in this post.
Comment by Larry — July 25, 2010 @ 8:27 am