Create property inside a model

Currently there’s no way to create a property inside a model method:


$this->$dynamicVar = 'some value';

This is because the setter is overridden by CActiveRecord. I need this to allow custom fields inside the register model such as name, age etc etc whatever the admin needs from new users so it’s a dynamic value all the time. Has anyone encountered this problem?

I’m not really sure I understand you correctly, but if I do, you could try:


$this->__set($dynamicVar, 'some value');

If that’s not what you mean, please describe your problem in a little more detail :)

That’s what I mean allright. The problem is that this magic function is overridden by CActiveRecord which of course is extended by my model:




public function __set($name,$value)

{

	if($this->setAttribute($name,$value)===false)

	{

		if(isset($this->getMetaData()->relations[$name]))

			$this->_related[$name]=$value;

		else

			parent::__set($name,$value);

	}

}


public function setAttribute($name,$value)

{

	if(property_exists($this,$name))

		$this->$name=$value;

	else if(isset($this->getMetaData()->columns[$name]))

		$this->_attributes[$name]=$value;

	else

		return false;

	return true;

}



As you can see, setAttribute returns false when you try to create a model property on the fly because property_exists($this,$name) returns false isset($this->getMetaData()->columns[$name]) returns false as well so the magic function __set does not set the property. So the function calls parent::__set($name,$value); so it goes to this:


public function __set($name,$value)

{

	$setter='set'.$name;

	if(method_exists($this,$setter))

		return $this->$setter($value);

	else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))

	{

		// duplicating getEventHandlers() here for performance

		$name=strtolower($name);

		if(!isset($this->_e[$name]))

			$this->_e[$name]=new CList;

		return $this->_e[$name]->add($value);

	}

	else if(is_array($this->_m))

	{

		foreach($this->_m as $object)

		{

			if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))

				return $object->$name=$value;

		}

	}

	if(method_exists($this,'get'.$name))

		throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',

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

	else

		throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',

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

}

The conclusion is, we can’t create properties inside a model. Should I create my own setter for this model? Is this safe?

I think I’d try something like this:




private $dynamicAttributes = array();


public function __set($attr, $value) {

    if(parent::__set($attr, $value)===false) {

        $this->dynamicAttributes[$attr] = $value;

    }

}


public function __get($attr) {

    return $this->__isset($attr) ? parent::__get($attr) : $this->dynamicAttributes[$attr];

}



Would that work for you?

Thanks, I appreciate it. Here’s what I ended up with:


	public function __get ( $name ) {

		if ( isset ( $this->dynAttributes [ $name ] ) ) return $this->dynAttributes [ $name ];

		else

			return parent::__get ( $name );

	}


	public function __set ( $attr, $value ) {

		try {

			parent::__set ( $attr, $value );

		} catch ( CException $e ) {

			$this->dynAttributes [ $attr ] = $value;

		}

	}

I use a try catch because CComponent throws an exception if __set fails.

Cool, that looks fine :)

For




private $somevalue;



in model class def this works for me:




function setSomevalue($value) {

  $this->somevalue = $value;

}

function getSomevalue($value) {

  return $this->somevalue;

}



That’s because you already declared it in the class: private $somevalue;

I retrieve those fields based on admin’s settings so I can’t declare them. I must create them on the fly. Try setting $this->somevalue = $value; without having it declared previously using private $somevalue;

Check this out:

Understanding Virtual Attributes and get/set methods

if you create a method like this in your model class:


class Person extends CActiveRecord {

   public function getFullName()

   {

      return $this->firstname . " " . $this->lastname;

   }

   ...

}

You can call it like normal ways:


// in a view somewhere

echo $form->dropDownList($model, 'personid',

    CHtml::listData( Person::model()->findAll(), 'id', 'fullname' )

);


$x = $model->fullname;

$x = $model->getFullname();        // same thing