diff --git a/Doctrine/EventListener.php b/Doctrine/EventListener.php index f6ded28ec..97bd6dd68 100644 --- a/Doctrine/EventListener.php +++ b/Doctrine/EventListener.php @@ -1,4 +1,23 @@ . + */ Doctrine::autoload('Doctrine_EventListener_Interface'); /** * Doctrine_EventListener all event listeners extend this base class @@ -30,6 +49,13 @@ abstract class Doctrine_EventListener implements Doctrine_EventListener_Interfac public function onSave(Doctrine_Record $record) { } public function onPreSave(Doctrine_Record $record) { } + public function onGetProperty(Doctrine_Record $record, $property, $value) { + return $value; + } + public function onPreSetProperty(Doctrine_Record $record, $property, $value) { + return $value; + } + public function onInsert(Doctrine_Record $record) { } public function onPreInsert(Doctrine_Record $record) { } diff --git a/Doctrine/EventListener/AccessorInvoker.php b/Doctrine/EventListener/AccessorInvoker.php new file mode 100644 index 000000000..6d11be4d5 --- /dev/null +++ b/Doctrine/EventListener/AccessorInvoker.php @@ -0,0 +1,78 @@ +. + */ + +/** + * Doctrine_EventListener_AccessorInvoker + * + * @author Konsta Vesterinen + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +class Doctrine_EventListener_AccessorInvoker extends Doctrine_EventListener { + /** + * @var boolean $lockGetCall a simple variable to prevent recursion + */ + private $lockGetCall = false; + /** + * @var boolean $lockSetCall a simple variable to prevent recursion + */ + private $lockSetCall = false; + /** + * onGetProperty + * + * @param Doctrine_Record $record + * @param string $property + * @param mixed $value + * @return mixed + */ + public function onGetProperty(Doctrine_Record $record, $property, $value) { + $method = 'get' . ucwords($property); + + if (method_exists($record, $method) && ! $this->lockGetCall) { + $this->lockGetCall = true; + + $value = $record->$method($value); + $this->lockGetCall = false; + return $value; + } + return $value; + } + /** + * onPreSetProperty + * + * @param Doctrine_Record $record + * @param string $property + * @param mixed $value + * @return mixed + */ + public function onPreSetProperty(Doctrine_Record $record, $property, $value) { + $method = 'set' . ucwords($property); + + if (method_exists($record, $method) && ! $this->lockSetCall) { + $this->lockSetCall = true; + $value = $record->$method($value); + $this->lockSetCall = false; + return $value; + } + return $value; + } +} diff --git a/Doctrine/EventListener/Interface.php b/Doctrine/EventListener/Interface.php index 188b1119b..41dc0213e 100644 --- a/Doctrine/EventListener/Interface.php +++ b/Doctrine/EventListener/Interface.php @@ -1,4 +1,4 @@ -data[$name])) throw new InvalidKeyException(); - if($this->data[$name] == self::$null) + if($this->data[$name] === self::$null) return null; return $this->data[$name]; } + /** + * load + * loads all the unitialized properties from the database + * + * @return boolean + */ + public function load() { + // only load the data from database if the Doctrine_Record is in proxy state + if($this->state == Doctrine_Record::STATE_PROXY) { + if( ! empty($this->collections)) { + // delegate the loading operation to collections in which this record resides + foreach($this->collections as $collection) { + $collection->load($this); + } + } else { + $this->refresh(); + } + $this->state = Doctrine_Record::STATE_CLEAN; + + return true; + } + return false; + } /** * get * returns a value of a property or a related component * - * @param $name name of the property or related component - * @throws InvalidKeyException + * @param mixed $name name of the property or related component + * @param boolean $invoke whether or not to invoke the onGetProperty listener + * @throws Doctrine_Exception * @return mixed */ - public function get($name) { + public function get($name, $invoke = true) { + $listener = $this->table->getAttribute(Doctrine::ATTR_LISTENER); + + $value = self::$null; + if(isset($this->data[$name])) { // check if the property is null (= it is the Doctrine_Null object located in self::$null) if($this->data[$name] === self::$null) { - // only load the data from database if the Doctrine_Record is in proxy state - if($this->state == Doctrine_Record::STATE_PROXY) { - if( ! empty($this->collections)) { - // delegate the loading operation to collections in which this record resides - foreach($this->collections as $collection) { - $collection->load($this); - } - } else { - $this->refresh(); - } - $this->state = Doctrine_Record::STATE_CLEAN; - } + $this->load(); if($this->data[$name] === self::$null) - return null; - } - return $this->data[$name]; + $value = null; + + } else + $value = $this->data[$name]; } if(isset($this->id[$name])) - return $this->id[$name]; + $value = $this->id[$name]; if($name === $this->table->getIdentifier()) - return null; + $value = null; + if($value !== self::$null) { + if($invoke) { + + return $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onGetProperty($this, $name, $value); + } else + return $value; + } if( ! isset($this->references[$name])) - $this->loadReference($name); + $this->loadReference($name); return $this->references[$name]; @@ -658,9 +683,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $value = $id; } - $old = $this->get($name); + $old = $this->get($name, false); if($old !== $value) { + + // invoke the onPreSetProperty listener + $value = $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreSetProperty($this, $name, $value); + if($value === null) $value = self::$null; diff --git a/tests/EventListenerTestCase.php b/tests/EventListenerTestCase.php index e0ea06810..fa2911d55 100644 --- a/tests/EventListenerTestCase.php +++ b/tests/EventListenerTestCase.php @@ -1,5 +1,20 @@ hasColumn("name", "string", 100); + $this->hasColumn("password", "string", 8); + } + public function setUp() { + $this->setAttribute(Doctrine::ATTR_LISTENER, new Doctrine_EventListener_AccessorInvoker()); + } + public function getName($name) { + return strtoupper($name); + } + public function setPassword($password) { + return md5($password); + } +} class Doctrine_EventListenerTestCase extends Doctrine_UnitTestCase { public function testEvents() { @@ -9,7 +24,45 @@ class Doctrine_EventListenerTestCase extends Doctrine_UnitTestCase { $this->assertTrue($last->getObject() instanceof Doctrine_Connection); $this->assertTrue($last->getCode() == Doctrine_EventListener_Debugger::EVENT_OPEN); } + public function testAccessorInvoker() { + $e = new EventListenerTest; + $e->name = "something"; + $e->password = "123"; + + + $this->assertEqual($e->get('name'), 'SOMETHING'); + // test repeated calls + $this->assertEqual($e->get('name'), 'SOMETHING'); + + $this->assertEqual($e->rawGet('name'), 'something'); + $this->assertEqual($e->password, '202cb962ac59075b964b07152d234b70'); + + $e->save(); + + $this->assertEqual($e->name, 'SOMETHING'); + $this->assertEqual($e->rawGet('name'), 'something'); + $this->assertEqual($e->password, '202cb962ac59075b964b07152d234b70'); + + $this->connection->clear(); + + $e->refresh(); + + $this->assertEqual($e->name, 'SOMETHING'); + $this->assertEqual($e->rawGet('name'), 'something'); + $this->assertEqual($e->password, '202cb962ac59075b964b07152d234b70'); + + $this->connection->clear(); + + $e = $e->getTable()->find($e->id); + + $this->assertEqual($e->name, 'SOMETHING'); + $this->assertEqual($e->rawGet('name'), 'something'); + $this->assertEqual($e->password, '202cb962ac59075b964b07152d234b70'); + } public function prepareData() { } - public function prepareTables() { } + public function prepareTables() { + $this->tables = array('EventListenerTest'); + parent::prepareTables(); + } } ?> diff --git a/tests/run.php b/tests/run.php index bb498be25..0776576fa 100644 --- a/tests/run.php +++ b/tests/run.php @@ -32,6 +32,8 @@ error_reporting(E_ALL); $test = new GroupTest("Doctrine Framework Unit Tests"); +$test->addTestCase(new Doctrine_EventListenerTestCase()); + $test->addTestCase(new Doctrine_RecordTestCase()); $test->addTestCase(new Doctrine_TableTestCase()); @@ -42,8 +44,6 @@ $test->addTestCase(new Doctrine_ManagerTestCase()); $test->addTestCase(new Doctrine_AccessTestCase()); -$test->addTestCase(new Doctrine_EventListenerTestCase()); - $test->addTestCase(new Doctrine_BatchIteratorTestCase()); $test->addTestCase(new Doctrine_ConfigurableTestCase());