Setting or overriding default model values

When dealing with a model or active record class, is there a way to override or set default values for attributes? I know that if default values were specified in the database schema those will be used, but how can I programatically re-define or set these if they weren't specified in the db? Thanks in advance for any help!

Declare a public member and set the default value.

Easy enough! It might be cool to have an additional function in the CModel class called attributeDefaults(), that could be overridden in an activerecord (or other) class like so:



public function attributeDefaults()


{


     return array(


        'attribute_name_1'=>null,


        'attribute_name_2'=>'default value',


        'attribute_name_3'=>100,


    );


}


Thanks for the help. I am really liking the Yii framework and have started evangelizing it to all of my friends.

I am not sure if attributeDefaults() is necessary since using public members can already set the defaults. Let's see if there are more requests for this. Thank you for recommending Yii to your friends.

Very true – the function is not needed, per se, since the attributes can be defined as public members of the class and set there. My rationale for adding it would be:

  1. From what I understand, Yii treats model members as a special type of class variable, called an attribute. By creating a function to set the default values of these members, or attributes, you thus further reinforce their designation of not being "normal" members of the class.

  2. Also, and somewhat in conjunction with my first point, since attributes already have other methods in place to modify them (their values or other associated data), like the attributeLabels() method, adding a function for attributeDefaults() would simply be keeping to that same standard or following suit to what has already been done.

As you state, though, the feature is trivial and doesn't really add function, just a little bit of form. =) Keep up the great work!

I actually just ran across another reason why a method to set default attribute values could actually provide some function. Suppose you wanted to assign a dynamic value to an attribute as its default – this can't be done in a class member declaration. You would have to use the constructor (or call code in another class method) to set the member's (attribute's) default value. This may not be a perfect example since there are probably better ways to assign a timestamp value to a var, but here's what I mean:



//THIS WON'T WORK -- dynamic value (function call) cannot be used in member assignment


class MyAr extends CActiveRecord


{


    public $signup_date = date('Y/m/d h:i:s');





    ....


}


Since that won't work, you would have to do something like this:



//WORKAROUND


class MyAr extends CActiveRecord


{


    public function __construct($attributes=array())


    {


        parent::__construct($attributes);


        $this->signup_date = date('Y/m/d h:i:s');


    }





    ....


}


It would just be cleaner to have a class method dedicated to setting default attribute values. Feel free to show me if there's a better way, though! =)

@luoshiben

That is not a valid example.

public $signup_date = date('Y/m/d h:i:s');

the above does not make sense.  That would make the signup date the time of the loading of the model in PHP (what if you have a really slow script between the model loading and the save()?).  It is better to do that in beforeSave().

You can also do the following (although it doesn't make much sense either):

getSignup_date() {

return date('Y/m/d h:i:s');

}

I would give a more valid example before expecting it to be considered.

@jonah

Thanks, Jonah. You are correct (as I also stated) that setting the default value of a member to a date string at time of object instantiation would be a little silly. I simply used it to illustrate my point that no assignment of dynamic data (be it date() or MyUtilityClass::customFunctionToGetDefaultValue()) can be made when declaring members. I could give you the scenario that I ran in to, but it wouldn't mean anything to anyone else since it is specific to my business logic. Either way, I don't think that one very specific case is a reason to add global functionality to the framework…which is why a generic example was used to illustrate the global principle.

Wouldn't it be possible to implement default values suing a filter rule i.e. extend CFilterValidator with a CDefaultValidator class.



    public function rules()


    {


        return array(


            array('username, password', 'required'),


            array('password', 'default','value'=>'some default value'),


        );


    }


This is a good idea. Any other people like this?

Is it ok for me to write a ticket for this. I probably will need it soon.

Btw, is it possible to retrieve all the rules for an attribute with some method?

Yes, please. thanks.

I agree with @luoshiben on that there must be another possible (and easy) way of defining a default value of an CActiveRecord-attribute easily since it's not possible to make function calls at the same time you declare a member variable of a class in PHP.

It would be fine to use database functions in the default values, like NOW() for a date(time) field. I saw another post about this, but can't find it.

Ok, feature is implemented.

You can use a rule in rules() to set a default value:



array('attr1, attr2', 'default', 'value'=>'something')


Since you can specify an 'on' option in a rule, this would allow an attribute to be set with different default values in different scenarios.

For SQL functions like NOW, you can do this:



array('attr1, attr2', 'default', 'value'=>new CDbExp​ression('NOW()'))


谢谢您, Qiang! This solution is great and much appreciated.

I know this is an older thread, but just some input on default values:

While having ability to set default values within validation rules is keen, that would only apply for the situation that validation is executed.

I dunno about anyone else, but there are times when I want to create a model without validation as it might be programmatically created. As a result, defaults would not be applied.

So the only real option then, I guess, is overriding beforeSave().

EDIT: or actually adding properties into the model, with default values set, which isn’t ideal if you’re used to only seeing properties in a model for non-data storage.