Hi there,
I’m writing a kind of activity feed, like the Facebook feed. I have a model (User) and would like to track whenever some information changes like ($user->name = ‘new value’). The optimal solution provides both the former and the later value of $user->name.
I thought I could just implement "public function setName", but it seems that db fields have precedence, which I think, is a very bad decision as CActiveRecord should be the layer between DB and logic, not just a forwarder.
My second thought was a “beforeSetAttribute”, but such an event doesn’t seems to exist. So I created a class called “ActiveRecord” to extend from with these functions:
/**
* PHP setter magic method.
* This method is overridden so that AR attributes can be accessed like properties.
* @param string $name property name
* @param mixed $value property value
*/
public function __set($name,$value) {
if ($this->beforeSetAttribute($name,$value)) {
parent::__set($name,$value);
$this->afterSetAttribute($name,$value);
}
}
/*public function setAttribute($name,$value) {
if ($this->beforeSetAttribute()) {
if (parent::setAttribute($name,$value))) {
$this->afterSetAttribute();
return true;
}
}
return false;
}*/
/**
* This event is raised before an attribute is set.
* By setting {@link CModelEvent::isValid} to be false, the normal {@link setAttribute()} process will be stopped.
* @param CModelEvent $event the event parameter
*/
public function onBeforeSetAttribute($event) {
$this->raiseEvent('onBeforeSetAttribute',$event);
}
/**
* This event is raised after an attribute is set.
* @param CEvent $event the event parameter
*/
public function onAfterSetAttribute($event) {
$this->raiseEvent('onAfterSetAttribute',$event);
}
/**
* This method is invoked before setting an attribute.
* The default implementation raises the {@link onBeforeSetAttribute} event.
* You may override this method to do any preparation work for setting an attribute.
* Make sure you call the parent implementation so that the event is raised properly.
* @return boolean whether the attribute should be updated. Defaults to true.
*/
protected function beforeSetAttribute($name,$value) {
if ($this->hasEventHandler('onBeforeSetAttribute')) {
$event = new CModelEvent($this);
$event->params = array('name' => $name, 'value' => $value);
$this->onBeforeSetAttribute($event);
return $event->isValid;
} else {
return true;
}
}
/**
* This method is invoked after setting an attribute successfully.
* The default implementation raises the {@link onAfterSetAttribute} event.
* You may override this method to do postprocessing after setting an attribute.
* Make sure you call the parent implementation so that the event is raised properly.
*/
protected function afterSetAttribute($name,$value) {
if ($this->hasEventHandler('onAfterSetAttribute')) {
$event = new CModelEvent($this);
$event->params = array('name' => $name, 'value' => $value);
$this->onAfterSetAttribute($event);
}
}
What do you think? How will this affect performance? Can it be done in a better way? Should something similar be implemented in Yii?