You are viewing revision #13 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
There are many ways to automate the setting of timestamps in yii ActiveRecord models. Three are presented here:
- Via rules()
- Via beforeSave()
- Via CTimestampBehavior (zii)
To start off we need to create a database table.
[sql]
CREATE TABLE IF NOT EXISTS `Nodes` (
`id` bigint(20) NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
);
After we have the table we need to create a model and CRUD for it by using Gii.
Check the guide on How to generate model and CRUD with Gii.
The first way you can do it is via your model's rules. Here is an example.
[php]
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
return array(
array('title','length','max'=>255),
array('title, created, modified', 'required'),
array('modified','default',
'value'=>new CDbExpression('NOW()'),
'setOnEmpty'=>false,'on'=>'update'),
array('created,modified','default',
'value'=>new CDbExpression('NOW()'),
'setOnEmpty'=>false,'on'=>'insert')
);
}
You see the two rules at the end, one changes the modified field when the record's being updated, and the other changes both fields when the record's being created. You'll also see the "new CDbExpression('NOW()')" statement. This passes "NOW()" to the MySQL server and it will not be escaped. MySQL will interpret it as a statement and not as a string. This means that the field types could have been any other date/time type (timestamp, etc.) and it would still work.
Another solution is to use beforeSave() as follows:
[php]
public function beforeSave() {
if ($this->isNewRecord)
$this->created = new CDbExpression('NOW()');
else
$this->modified = new CDbExpression('NOW()');
return parent::beforeSave();
}
Note that in the above code, when creating a new record only the 'created' field will be assigned/updated, while the 'modified' record will be assigned/updated only when updating an existing record.
If you want to assign/update the 'modified' field even when creating a new record... use this code:
[php]
public function beforeSave() {
if ($this->isNewRecord)
$this->created = new CDbExpression('NOW()');
$this->modified = new CDbExpression('NOW()');
return parent::beforeSave();
}
These are simple and elegant solutions to this issue.
The third possibility is to use CTimestampBehavior in your models. You can read about this in the API documentation: http://www.yiiframework.com/doc/api/1.1/CTimestampBehavior
Yii 1.1
We will have this in the core for now. \o/
http://www.yiiframework.com/doc/api/CTimestampBehavior
PHP date() VS MySQL NOW()
It should be noted that MySQL has very advanced timestamp handling. If you were to use timestamp instead of datetime as the type of the database fields you would get several advantages.
Firstly, MySQL stores an internal timezone variable. All timestamps passed via a query are converted to the UTC for the server's timezone then are converted back to the client's timezone upon retrieval.
This make it so that if you handle timezone configurations on PHP side properly, all dates on your website can automatically be translated into the timezone of the current user's configuration.
Also, and you can do this with datetime fields as well, the most reliable way of using a modified field inside a MySQL is to declare the field as follows.
modified
timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPWith the configuration like this, MySQL will automatically handle the manipulation of this field and you won't have to worry about it application-side.
You should be aware of the timestamp caveats, though, when using InnoDB if you are going to. Docs on timestamp fields can be found here: http://dev.mysql.com/doc/refman/5.0/en/timestamp.html
It should be noted
I would like to point out that the second method does not set the modified timestamp when a new record is being created. The best method to do this would be simply to remove the 'else' keyword. That way modified is always set, and created is only set when this is a new record, as follows.
public function beforeSave() {
if ($this->isNewRecord) $this->created = new CDbExpression('NOW()'); $this->modified = new CDbExpression('NOW()'); return parent::beforeSave();
}
It should also be noted, that this only works when you use the active record and do save() events. When you do mass updates with SQL statements, this method has no effect.
beforeSave or beforeValidate
Dear All
I have applied both of the above mentioned methods, and none worked.
Why? Because I was trying to fill the fields marked as required.
Both the rules and beforeSave() functions work after validation, and for required fields, the value being empty does not validates the object, hence unable to save it.
The work around is quite simple, I used the beforeValidate Function, which works before validation, and the ar model validates perfectly and saves smoothly.
Hope this should be included in the guide itself and also the points that rules are also applied after validating the ar model.
beforeValidate Does Not Saves the model
Well sorry for my previous post.
Setting the values in beforeValidate() function validates the ar model perfectly, but refuses to save it, no matter what did, and that also without giving any error.
A total Mystery, maybe anyone can help.
I switched back to init() function for initializing some of the fields to their default value.
Rules and beforeSave
@anisrehan
rules should only be used for user input. That is, if you specify the beforeSave, you can take create_time etc. out of your 'required' rules, since they are no longer entered in the form (and since you have written the code, they will always be added). This is also why the 'default' rule to set the time is not exactly best practice.
UTC_TIMESTAMP()
Instead of setting mysqlto UTC and using NOW(), you can use UTC_TIMESTAMP().
Adding rules
Am using " on->create" on rules for validation only in the create page. i got an error. after i read this article i cleared my error as " on-> insert" thanks for you article.. and thanks for the yii team..
After call beforeSave() then don't forget to return true
public function beforeSave(){
if ($this->isNewRecord) $this->created_date = new CDbExpression('NOW()'); else{ $this->modified = new CDbExpression('NOW()'); } parent::beforeSave(); return true; }
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.