Attributes, Labels, Validation Rules Etc. Dynamically From Database

Hi!

I am considering porting my application to Yii.

The app. consist of several related tables (i.e. tbl_persons) which hold the data and a meta-info (tbl_metaInfo) table, where I have validation rules, attribute labels etc. Currently the app generates all the UI based on the database definition automatically. For example tbl_persons has the fields firstName, lastName, eMail defined as text (40), not null and in tbl_metaInfo I defined a regular expression that the eMail should be validated against. Also I have multi-language attribute names in a separate table. Based on this app generates all the CRUD (forms with appropriate lengths and type) for persons and loads the rules, labels from separate tables.

In addition, when I add a field in tbl_persons (i.e. nickName) the application automatically show it in the UI and nothing brakes. Only thing hard-coded is the name of the table (tbl_persons) everything else is generated dynamically.

Can Yii handle something similar and what is the correct way to implement it in Yii?

I will be grateful for any help or insight you can provide.

You can write this dynamic loading into rules(), attributeNames() and attributeLabels() methods.

You can also use cache for the dynamically loaded data.

Thank you!

From the MVC perspective, is it correct then to implement a separate model for rules and labels and integrate this into the rules method of other models?

Yes, that’s perfectly ok.

You may want to create behaviors for this. Info on using them here.

Can you explain (or point to a link) in more detail how to add custom validation to model with behavior. I have tried reading the docs and searching the net, but as I am quite new to Yii and frameworks, it is still very confusing. As far as I understand from re-usability perspective, the behavior approach would be much better.

Using behaviors:

  • What would be steps to add custom rules to a model from database table (another Model?)?

  • What would be steps to add custom attribute labels and also relationships?

My progress so far, I have tried to implement custom rules (loaded from database) this way. Please comment and tell my why is this approach bad / wrong ?

  1. Created "MyRules" a Model based on AR (it is also good to have a CRUD) based on database:

tbl_myrules

-id

-model_name

-attribute

-pattern

-message

  1. Added a new method to MyRyles model, which returns a AR rules() compatible array.

public function getDRules($model_name) {

	$dRules = $this->findAllByAttributes(array('model_name' => $model_name));

	foreach ($dRules as $dR) {

		$rv[] = array($dR->attribute, 'match', 'pattern'=>$dR->regEx, 'message' => $dR->message);

	}

	return $rv;

}

  1. In model where I need to have custom validation i changed the rules method() as follows:

public function rules() {

    return array_merge(

                    array(

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

                array('content', 'safe'),

                // The following rule is used by search().

                // Please remove those attributes that should not be searched.

                array('id, content, number', 'safe', 'on' => 'search'),

                    ), MyRules::model()->getDRules(get_class())

    );

}

And I think I could similarly load the custom attribute labels and relationships from database dynamically.

For custom rules, since rules is a method, in each model you can merge its own rules with the behavior’s rules and return them. You need to create a method in your behavior to return the common rules.

This is the best approach I can think of, because behaviors can’t override their owner’s methods.

Regarding attribute labels, you can do the same.

Also, please take a look at my extension, giix. You can set the attribute names of the models only once and get all related models to use them.

Nothing wrong with getting your rules from your database if they are dynamic. If they’re static, though, I’d prefer to have everything on files.

I do not see how could you merge common rules from the behavior with model rules, as you cannot overwrite the model’s original rules method.

So I figured, solution using behaviors would be to create a beforeValidate() or afterValidate() method in my behavior class and validate all attribute by hand like this:


class MyRulesBehavior extends CActiveRecordBehavior {


	public function beforeValidate($event){

		// ...validation code goes here  

		if(true)

			$event->isValid = true;

		else

			$event->isValid = false;

		

		

		return $event->isValid;

	}

	

}