Creating my own model (CModel NOT CActiveRecord)

Hello,

I have created my own Model. And when I wanted to validate a form I created I got




RegisterPublisherForm and its behaviors do not have a method or closure named "getDbConnection". 


/var/www/yii/framework/base/CComponent.php(266)


264         if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure)

265             return call_user_func_array($this->$name, $parameters);

266         throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".',

267             array('{class}'=>get_class($this), '{name}'=>$name)));



How should I implement a uniqe field validator in a model that is not an Active Record?

please tell me if there is something else that I need to post here in order to get a more focused answer

Thanks!

You have to specify the model name and the attribute name by ‘className’ and ‘attributeName’ when you want to use CUniqueValidator in CFormModel or in another CActiveRecord model.

http://www.yiiframework.com/doc/api/1.1/CUniqueValidator/




    array('xxx', 'unique', 'className' => 'SomeClass', 'attributeName' => 'SomeAttribute'),



I did that. the problem was different - I used CHTML::activeTextField instead on CHTML::textField

Thanks

??

I’m sorry, but I don’t understand what you mean by that.

I don’t think it makes any difference regarding the validation whether you will use CHtml::activeTextField or CHtml::textField, as long as the massive assignment of the attributes are properly processed.

Just to double check, did you assign a valid CActiveRecord class name other than ‘RegisterPublisherForm’ for ‘className’?

Sorry, but somthing is just wrong… I thought I understood it - but appearantly I didn’t:

I have the ‘RegisterPublisherForm’ Class who extends CModel,

It has it’s validators, one of them is ‘uniqe’… is that a validator that only CActiveRecord can use?

It keep giving me an exception




'Property "RegisterPublisherForm._md" is not defined.'

from

/var/www/yii/framework/db/ar/CActiveRecord.php(379)



the model validation function is always failing. Why would it try to use an ActiveRecord when I don’t want to use it, and I didn’t even extend from it…

here is (part of) the stack trace log:




/var/www/yii/framework/db/ar/CActiveRecord.php(379): CComponent->__set("_md", CActiveRecordMetaData)


374         if(isset(self::$_models[$className]))

375             return self::$_models[$className];

376         else

377         {

378             $model=self::$_models[$className]=new $className(null);

379             $model->_md=new CActiveRecordMetaData($model);

380             $model->attachBehaviors($model->behaviors());

381             return $model;

382         }

383     }

384 




	 /var/www/yii/framework/validators/CUniqueValidator.php(87): CActiveRecord::model("RegisterPublisherForm")


82         if($this->allowEmpty && $this->isEmpty($value))

83             return;

84 

85         $className=$this->className===null?get_class($object):Yii::import($this->className);

86         $attributeName=$this->attributeName===null?$attribute:$this->attributeName;

87         $finder=CActiveRecord::model($className);

88         $table=$finder->getTableSchema();

89         if(($column=$table->getColumn($attributeName))===null)

90             throw new CException(Yii::t('yii','Table "{table}" does not have a column named "{column}".',

91                 array('{column}'=>$attributeName,'{table}'=>$table->name)));


/var/www/yii/framework/validators/CValidator.php(197): CUniqueValidator->validateAttribute(RegisterPublisherForm, "email")


192         else

193             $attributes=$this->attributes;

194         foreach($attributes as $attribute)

195         {

196             if(!$this->skipOnError || !$object->hasErrors($attribute))

197                 $this->validateAttribute($object,$attribute);

198         }

199     }

200 



the "email" is the uniqe one…

CUniqueValidator needs a CActiveRecord in order to compare the user input against the existing values of a column in a specific db table.

You can omit ‘className’ and ‘attributeName’. They default to the model name and the attribute name.




// in User.cpp

public function rules()

{

    return array(

        array('email', 'unique');

        ...

    );

}



And you can use it in another CActiveRecord and/or in a CFormModel by explicitly specifying ‘className’ and ‘attributeName’.




// in RegisterPublisherForm.cpp

public function rules()

{

    return array(

        array('email', 'unique', 'className' => 'User');

        ...

        or

        ...

        array('emailAddress', 'unique', 'className' => 'User', 'attributeName' => 'email');

        ...

    );

}



I don’t know if CModel and CFormModel behave differently in this. I didn’t have a chance to use CModel.

But I don’t extend CActiveRecord and the uniqe validator is set like that:




...

array('email', 'unique', 'message' => User::t("This user's email address already exists.")),

...



I wanted to create my own uniqe validator but i read that it could make trouble if they have the same name.

The question is simple:

How to create a uniqe validator in a CModel model?

if it is not possible than I will implement my own and give it a different name, but I would rather use the standard name.

just add method in model and use it as a validator. Then put all needed logic in this method:




public function rules()

{

    return array(

        array('email', 'myUniqueValidator');

    );

}


public function myUniqueValidator( $attributeName, $params ) {

  if( $this->$atributeName is not unique ) {

    $this->addError( $attributeName, 'must be unique!!!' );

  }

}



easy as that. just remember that validation method does not need to return anything (especially boolean as validation result). It is just required that you add error with addError method.

I don’t understand what you mean by “extend CActiveRecord” …

Your “RegisterPublisherForm” is not extended from CActiveRecord, that’s OK. There’s no problem in that.

But you will need to look up some db table in order to check the uniqueness of the input email.

And I think that you should have already created a CActiveRecord model for that table.

Or, you don’t have a CActiveRecord class for it?

And you are going to save the input email address to the table all by DAO/SQL?

If you already have a CActiveRecord class for the email data, then all you have to do should be specifying the name of it for ‘className’:




    array('email', 'unique', 'className' => 'User', 'message' => User::t("This user's email address already exists."));



In the above, I assume the class name is ‘User’, but it may be a different name.

I’m sorry if I don’t get your needs right.

Yes I’m using DAO.

And my class is extending from Cmodel, that mean it is not an active record class model.

For now - I implemented my own validator, but I would like to use the ‘unique’ keyword as a validator.

As I mentioned - I have implemented a custom validator in the form as my previous post, so from super critical it became a ‘nice to have’ feature.

Thanks for the help.

Ah, all right!

So you are building your app with no CActiveRecord models at all … I’m sorry, I couldn’t imagine the possibility of it.

But, it might be a hard way, I’m afraid.

IMO, there’s no need to avoid using CActiveRecord … it is quite handy and do 90% of your database job sufficiently effective. And it’s also very easy to use DAO or plain SQL within CActiveRecord extended class when you really need to achieve good performance.