Yii 1.1: How to automate timestamps in ActiveRecord models

36 followers

There are many ways to automate the setting of timestamps in yii ActiveRecord models. Three are presented here:

  1. Via rules()
  2. Via beforeSave()
  3. Via CTimestampBehavior (zii)

To start off we need to create a database table.

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.

/**
 * @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:

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:

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

Total 8 comments

#17501 report it
vijay p s at 2014/06/24 12:50am
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..

#9837 report it
tadas at 2012/09/14 07:08am
UTC_TIMESTAMP()

Instead of setting mysqlto UTC and using NOW(), you can use UTC_TIMESTAMP().

#7644 report it
Narretz at 2012/04/04 01:42pm
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.

#5028 report it
Rehan Anis at 2011/09/07 11:05am
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.

#5019 report it
Rehan Anis at 2011/09/07 02:51am
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.

#1276 report it
Ismael at 2009/10/02 12:45pm
Yii 1.1

We will have this in the core for now. \o/

http://www.yiiframework.com/doc/api/CTimestampBehavior

#1410 report it
killermonk at 2009/08/08 09:20pm
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_TIMESTAMP

With 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

#1411 report it
killermonk at 2009/08/08 09:14pm
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.

Leave a comment

Please to leave your comment.

Write new article
  • Written by: dalip
  • Updated by: Yang He
  • Category: Tutorials
  • Yii Version: 1.1
  • Votes: +30
  • Viewed: 102,285 times
  • Created on: Feb 14, 2009
  • Last updated: Jun 29, 2012