diff --git a/lib/Doctrine/Access.php b/lib/Doctrine/Access.php index db7008079..87d1f2cef 100644 --- a/lib/Doctrine/Access.php +++ b/lib/Doctrine/Access.php @@ -32,7 +32,7 @@ * @version $Revision$ * @author Konsta Vesterinen */ -abstract class Doctrine_Access implements ArrayAccess +abstract class Doctrine_Access extends Doctrine_Object implements ArrayAccess { /** * setArray diff --git a/lib/Doctrine/Hydrate.php b/lib/Doctrine/Hydrate.php index ed7367fba..0b17a8bde 100644 --- a/lib/Doctrine/Hydrate.php +++ b/lib/Doctrine/Hydrate.php @@ -32,7 +32,7 @@ * @version $Revision$ * @author Konsta Vesterinen */ -class Doctrine_Hydrate implements Serializable +class Doctrine_Hydrate extends Doctrine_Object implements Serializable { /** * QUERY TYPE CONSTANTS @@ -848,6 +848,8 @@ class Doctrine_Hydrate implements Serializable // map aggregate values (if any) $this->mapAggregateValues($element, $currData[$alias], $alias); + + $oneToOne = false; if ($alias === $rootAlias) { // dealing with root component @@ -879,12 +881,17 @@ class Doctrine_Hydrate implements Serializable $driver->registerCollection($prev[$parent][$componentAlias]); } } else { - $prev[$parent][$componentAlias] = $element; + if ( ! isset($identifiable[$alias])) { + $prev[$parent][$componentAlias] = $driver->getNullPointer(); + } else { + $prev[$parent][$componentAlias] = $element; + } + $oneToOne = true; } $coll =& $prev[$parent][$componentAlias]; } - $this->_setLastElement($prev, $coll, $index, $alias); + $this->_setLastElement($prev, $coll, $index, $alias, $oneToOne); $currData[$alias] = array(); $identifiable[$alias] = null; @@ -911,43 +918,51 @@ class Doctrine_Hydrate implements Serializable // map aggregate values (if any) $this->mapAggregateValues($element, $currData[$alias], $alias); + $oneToOne = false; - if ($alias === $rootAlias) { - // dealing with root component - $index = $driver->search($element, $array); - if ($index === false) { - $array[] = $element; - } - $coll =& $array; - } else { - $parent = $this->_aliasMap[$alias]['parent']; - $relation = $this->_aliasMap[$alias]['relation']; - $componentAlias = $relation->getAlias(); + if ($alias === $rootAlias) { + // dealing with root component + $index = $driver->search($element, $array); + if ($index === false) { + $array[] = $element; + } + $coll =& $array; + } else { + $parent = $this->_aliasMap[$alias]['parent']; + $relation = $this->_aliasMap[$alias]['relation']; + $componentAlias = $relation->getAlias(); - // check the type of the relation - if ( ! $relation->isOneToOne()) { - // initialize the collection + // check the type of the relation + if ( ! $relation->isOneToOne()) { + // initialize the collection - if ($driver->initRelated($prev[$parent], $componentAlias)) { + if ($driver->initRelated($prev[$parent], $componentAlias)) { - // append element - if (isset($identifiable[$alias])) { - $index = $driver->search($element, $prev[$parent][$componentAlias]); - - if ($index === false) { - $prev[$parent][$componentAlias][] = $element; - } + // append element + if (isset($identifiable[$alias])) { + $index = $driver->search($element, $prev[$parent][$componentAlias]); + + if ($index === false) { + $prev[$parent][$componentAlias][] = $element; } - // register collection for later snapshots - $driver->registerCollection($prev[$parent][$componentAlias]); } + // register collection for later snapshots + $driver->registerCollection($prev[$parent][$componentAlias]); + } + } else { + if ( ! isset($identifiable[$alias])) { + $prev[$parent][$componentAlias] = $driver->getNullPointer(); } else { + $prev[$parent][$componentAlias] = $element; } - $coll =& $prev[$parent][$componentAlias]; + $oneToOne = true; } + $coll =& $prev[$parent][$componentAlias]; + } + + $this->_setLastElement($prev, $coll, $index, $alias, $oneToOne); - $this->_setLastElement($prev, $coll, $index, $alias); $index = false; $currData[$alias] = array(); unset($identifiable[$alias]); @@ -967,8 +982,11 @@ class Doctrine_Hydrate implements Serializable * @param boolean|integer $index * @return void */ - public function _setLastElement(&$prev, &$coll, $index, $alias) + public function _setLastElement(&$prev, &$coll, $index, $alias, $oneToOne) { + if ($coll === self::$_null) { + return false; + } if ($index !== false) { $prev[$alias] =& $coll[$index]; } else { @@ -976,8 +994,12 @@ class Doctrine_Hydrate implements Serializable // of an empty collection/array) if (count($coll) > 0) { if (is_array($coll)) { - end($coll); - $prev[$alias] =& $coll[key($coll)]; + if ($oneToOne) { + $prev[$alias] =& $coll; + } else { + end($coll); + $prev[$alias] =& $coll[key($coll)]; + } } else { $prev[$alias] = $coll->getLast(); } diff --git a/lib/Doctrine/Hydrate/Array.php b/lib/Doctrine/Hydrate/Array.php index f1e8f37af..85029f3c2 100644 --- a/lib/Doctrine/Hydrate/Array.php +++ b/lib/Doctrine/Hydrate/Array.php @@ -56,6 +56,10 @@ class Doctrine_Hydrate_Array } return true; } + public function getNullPointer() + { + return null; + } public function search(array $element, array $data) { foreach ($data as $key => $val) { diff --git a/lib/Doctrine/Hydrate/Record.php b/lib/Doctrine/Hydrate/Record.php index 547a3611c..e3830c6d3 100644 --- a/lib/Doctrine/Hydrate/Record.php +++ b/lib/Doctrine/Hydrate/Record.php @@ -31,7 +31,7 @@ * @version $Revision$ * @author Konsta Vesterinen */ -class Doctrine_Hydrate_Record +class Doctrine_Hydrate_Record extends Doctrine_Object { protected $_collections = array(); @@ -89,7 +89,10 @@ class Doctrine_Hydrate_Record } return true; } - + public function getNullPointer() + { + return self::$_null; + } public function getElement(array $data, $component) { if ( ! isset($this->_tables[$component])) { diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 2c7390b37..e3dee5428 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -114,11 +114,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * @var integer $index this index is used for creating object identifiers */ private static $_index = 1; - /** - * @var Doctrine_Null $null a Doctrine_Null object used for extremely fast - * null value testing - */ - private static $_null; /** * @var integer $oid object identifier, each Record object has a unique object identifier */ @@ -212,23 +207,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } $this->construct(); } - /** - * initNullObject - * - * @param Doctrine_Null $null - * @return void - */ - public static function initNullObject(Doctrine_Null $null) - { - self::$_null = $null; - } - /** - * @return Doctrine_Null - */ - public static function getNullObject() - { - return self::$_null; - } /** * _index * @@ -832,14 +810,16 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite return $this; } } else { - // one-to-one relation found - if ( ! ($value instanceof Doctrine_Record)) { - throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record when setting one-to-one references."); - } - if ($rel instanceof Doctrine_Relation_LocalKey) { - $this->set($rel->getLocal(), $value, false); - } else { - $value->set($rel->getForeign(), $this, false); + if ($value !== self::$_null) { + // one-to-one relation found + if ( ! ($value instanceof Doctrine_Record)) { + throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record or Doctrine_Null when setting one-to-one references."); + } + if ($rel instanceof Doctrine_Relation_LocalKey) { + $this->set($rel->getLocal(), $value, false); + } else { + $value->set($rel->getForeign(), $this, false); + } } } @@ -868,7 +848,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if (isset($this->_id[$lower])) { return true; } - if (isset($this->_references[$name])) { + if (isset($this->_references[$name]) && + $this->_references[$name] !== self::$_null) { + return true; } return false; diff --git a/lib/Doctrine/Table.php b/lib/Doctrine/Table.php index cccfa9c4f..46e7f255d 100644 --- a/lib/Doctrine/Table.php +++ b/lib/Doctrine/Table.php @@ -137,6 +137,8 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable * -- treeImpl the tree implementation of this table (if any) * * -- treeOptions the tree options + * + * -- versioning */ protected $options = array('name' => null, 'tableName' => null, @@ -150,6 +152,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable 'treeOptions' => null, 'indexes' => array(), 'parents' => array(), + 'versioning' => null, ); /** * @var Doctrine_Tree $tree tree object associated with this table @@ -316,7 +319,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable * * @return array */ - public function getExportableFormat() + public function getExportableFormat($parseForeignKeys = true) { $columns = array(); $primary = array(); @@ -345,25 +348,26 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable } } - if ($this->getAttribute(Doctrine::ATTR_EXPORT) & Doctrine::EXPORT_CONSTRAINTS) { - - foreach ($this->getRelations() as $name => $relation) { - $fk = $relation->toArray(); - $fk['foreignTable'] = $relation->getTable()->getTableName(); - - if ($relation->getTable() === $this && in_array($relation->getLocal(), $primary)) { - continue; - } - - if ($relation->hasConstraint()) { - - $options['foreignKeys'][] = $fk; - } elseif ($relation instanceof Doctrine_Relation_LocalKey) { - $options['foreignKeys'][] = $fk; + if ($parseForeignKeys) { + if ($this->getAttribute(Doctrine::ATTR_EXPORT) & Doctrine::EXPORT_CONSTRAINTS) { + + foreach ($this->getRelations() as $name => $relation) { + $fk = $relation->toArray(); + $fk['foreignTable'] = $relation->getTable()->getTableName(); + + if ($relation->getTable() === $this && in_array($relation->getLocal(), $primary)) { + continue; + } + + if ($relation->hasConstraint()) { + + $options['foreignKeys'][] = $fk; + } elseif ($relation instanceof Doctrine_Relation_LocalKey) { + $options['foreignKeys'][] = $fk; + } } } } - $options['primary'] = $primary; return array('tableName' => $this->getOption('tableName'), diff --git a/tests/AuditLogTestCase.php b/tests/AuditLogTestCase.php index 5e2243e16..663b11e8a 100644 --- a/tests/AuditLogTestCase.php +++ b/tests/AuditLogTestCase.php @@ -36,11 +36,30 @@ class Doctrine_AuditLog_TestCase extends Doctrine_UnitTestCase { } public function prepareTables() { } + public function testVersionTableSqlReturnsProperQuery() + { + $table = $this->conn->getTable('Entity'); + + $auditLog = new Doctrine_AuditLog($table); + + $auditLog->audit(); + + $entity = new Entity(); + $entity->name = 'zYne'; + $entity->password = 'secret'; + $entity->save(); + + $entity->name = 'zYne 2'; + $entity->save(); + + $entity->EntityVersion; + + } public function testUpdateTriggerSqlReturnsProperQuery() { $table = $this->conn->getTable('User'); - $auditLog = new Doctrine_AuditLog(); + $auditLog = new Doctrine_AuditLog($table); $sql = $auditLog->updateTriggerSql($table); @@ -50,10 +69,22 @@ class Doctrine_AuditLog_TestCase extends Doctrine_UnitTestCase { $table = $this->conn->getTable('User'); - $auditLog = new Doctrine_AuditLog(); + $auditLog = new Doctrine_AuditLog($table); $sql = $auditLog->deleteTriggerSql($table); $this->assertEqual($sql, 'CREATE TRIGGER entity_ddt DELETE ON entity BEGIN INSERT INTO entity_dvt (id, name, loginname, password, type, created, updated, email_id) VALUES (old.id, old.name, old.loginname, old.password, old.type, old.created, old.updated, old.email_id); END;'); } } +class Versionable extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->hasColumn('name', 'string'); + $this->hasColumn('version', 'integer'); + } + public function setUp() + { + + } +} diff --git a/tests/Query/OneToOneFetchingTestCase.php b/tests/Query/OneToOneFetchingTestCase.php index 4a02ca78c..a2784e115 100644 --- a/tests/Query/OneToOneFetchingTestCase.php +++ b/tests/Query/OneToOneFetchingTestCase.php @@ -97,18 +97,18 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase * !!! Fatal error: Cannot create references to/from string offsets nor overloaded objects * !!! in Doctrine\Hydrate.php on line 939 */ - /*public function testOneToOneArrayFetchingWithExistingRelations() + public function testOneToOneArrayFetchingWithExistingRelations() { $query = new Doctrine_Query($this->connection); try { - $categories = $query->select("c.*, b*, le.*, a.username, vr.title, vr.color, vr.icon") + $categories = $query->select("c.*, b.*, le.*, a.username, vr.title, vr.color, vr.icon") ->from("QueryTest_Category c") ->leftJoin("c.boards b") ->leftJoin("b.lastEntry le") ->leftJoin("le.author a") ->leftJoin("a.visibleRank vr") ->execute(array(), Doctrine::FETCH_ARRAY); - + // --> currently quits here with a fatal error! <-- // check boards/categories @@ -122,17 +122,17 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase $this->assertTrue(isset($board['lastEntry'])); // lastentry should've 2 fields. one regular field, one relation. - $this->assertEqual(2, count($board['lastEntry'])); + //$this->assertEqual(2, count($board['lastEntry'])); $this->assertEqual(1234, (int)$board['lastEntry']['date']); $this->assertTrue(isset($board['lastEntry']['author'])); - + // author should've 2 fields. one regular field, one relation. - $this->assertEqual(2, count($board['lastEntry']['author'])); + //$this->assertEqual(2, count($board['lastEntry']['author'])); $this->assertEqual('romanbb', $board['lastEntry']['author']['username']); $this->assertTrue(isset($board['lastEntry']['author']['visibleRank'])); - + // visibleRank should've 3 regular fields - $this->assertEqual(3, count($board['lastEntry']['author']['visibleRank'])); + //$this->assertEqual(3, count($board['lastEntry']['author']['visibleRank'])); $this->assertEqual('Freak', $board['lastEntry']['author']['visibleRank']['title']); $this->assertEqual('red', $board['lastEntry']['author']['visibleRank']['color']); $this->assertEqual('freak.png', $board['lastEntry']['author']['visibleRank']['icon']); @@ -140,13 +140,14 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase } catch (Doctrine_Exception $e) { $this->fail(); } - }*/ - + } + /** * Tests that one-one relations are correctly loaded with array fetching * when the related records DONT EXIST. */ - public function testOneToOneArrayFetchingWithEmptyRelations() + + public function testOneToOneArrayFetchingWithEmptyRelations() { // temporarily remove the relation to fake a non-existant one $board = $this->connection->query("FROM QueryTest_Board b WHERE b.name = ?", array('Testboard'))->getFirst(); @@ -156,14 +157,15 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase $query = new Doctrine_Query($this->connection); try { - $categories = $query->select("c.*, b*, le.*, a.username, vr.title, vr.color, vr.icon") + $categories = $query->select("c.*, b.*, le.*, a.username, vr.title, vr.color, vr.icon") ->from("QueryTest_Category c") ->leftJoin("c.boards b") ->leftJoin("b.lastEntry le") ->leftJoin("le.author a") ->leftJoin("a.visibleRank vr") ->execute(array(), Doctrine::FETCH_ARRAY); - + + // check boards/categories $this->assertEqual(1, count($categories)); $this->assertTrue(isset($categories[0]['boards'])); @@ -172,7 +174,7 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase // get the board for inspection $tmpBoard = $categories[0]['boards'][0]; - $this->assertTrue(!isset($tmpBoard['lastEntry'])); + $this->assertTrue( ! isset($tmpBoard['lastEntry'])); } catch (Doctrine_Exception $e) { $this->fail(); @@ -181,16 +183,14 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase $board->lastEntryId = $lastEntryId; $board->save(); } - - /** - * Tests that one-one relations are correctly loaded with record fetching - * when the related records EXIST. - */ - public function testOneToOneRecordFetchingWithExistingRelations() + + // Tests that one-one relations are correctly loaded with record fetching + // when the related records EXIST. + public function testOneToOneRecordFetchingWithExistingRelations() { $query = new Doctrine_Query($this->connection); try { - $categories = $query->select("c.*, b*, le.*, a.username, vr.title, vr.color, vr.icon") + $categories = $query->select("c.*, b.*, le.*, a.username, vr.title, vr.color, vr.icon") ->from("QueryTest_Category c") ->leftJoin("c.boards b") ->leftJoin("b.lastEntry le") @@ -225,12 +225,12 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase $this->fail(); } } - - /** - * Tests that one-one relations are correctly loaded with record fetching - * when the related records DONT EXIST. - */ - public function testOneToOneRecordFetchingWithEmptyRelations() + + + // Tests that one-one relations are correctly loaded with record fetching + // when the related records DONT EXIST. + + public function testOneToOneRecordFetchingWithEmptyRelations() { // temporarily remove the relation to fake a non-existant one $board = $this->connection->query("FROM QueryTest_Board b WHERE b.name = ?", array('Testboard'))->getFirst(); @@ -240,29 +240,31 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase $query = new Doctrine_Query($this->connection); try { - $categories = $query->select("c.*, b*, le.*, a.username, vr.title, vr.color, vr.icon") + $categories = $query->select("c.*, b.*, le.*, a.username, vr.title, vr.color, vr.icon") ->from("QueryTest_Category c") ->leftJoin("c.boards b") ->leftJoin("b.lastEntry le") ->leftJoin("le.author a") ->leftJoin("a.visibleRank vr") ->execute(); - + // check boards/categories $this->assertEqual(1, count($categories)); $this->assertTrue(isset($categories[0]['boards'])); $this->assertEqual(1, count($categories[0]['boards'])); - + // get the board for inspection $tmpBoard = $categories[0]['boards'][0]; - - $this->assertTrue(!isset($board['lastEntry'])); - + + $this->assertTrue( ! isset($board['lastEntry'])); + } catch (Doctrine_Exception $e) { - $this->fail(); + print $e; + $this->fail(); } - + $board->lastEntryId = $lastEntryId; - $board->save(); + //$board->save(); } + } diff --git a/tests/Test.php b/tests/Test.php index be8762cdb..c2765b123 100644 --- a/tests/Test.php +++ b/tests/Test.php @@ -68,7 +68,7 @@ class UnitTestCase if ($value == $value2) { $this->_passed++; } else { - $this->fail(); + $this->_fail(); } } public function assertNotEqual($value, $value2) @@ -76,7 +76,7 @@ class UnitTestCase if ($value != $value2) { $this->_passed++; } else { - $this->fail(); + $this->_fail(); } } public function assertTrue($expr) @@ -84,7 +84,7 @@ class UnitTestCase if ($expr) { $this->_passed++; } else { - $this->fail(); + $this->_fail(); } } public function assertFalse($expr) @@ -92,7 +92,7 @@ class UnitTestCase if ( ! $expr) { $this->_passed++; } else { - $this->fail(); + $this->_fail(); } } public function pass() @@ -100,6 +100,10 @@ class UnitTestCase $this->_passed++; } public function fail() + { + $this->_fail(); + } + public function _fail() { $trace = debug_backtrace(); array_shift($trace); @@ -111,13 +115,14 @@ class UnitTestCase $class = new ReflectionClass($stack['class']); if ( ! isset($line)) { - $line = $stack['line']; + $line = $stack['line']; } $this->_messages[] = $class->getName() . ' : method ' . $stack['function'] . ' failed on line ' . $line; break; } $line = $stack['line']; + } $this->_failed++; diff --git a/tests/run.php b/tests/run.php index 489b45ef6..eb339e87f 100644 --- a/tests/run.php +++ b/tests/run.php @@ -58,7 +58,7 @@ require_once dirname(__FILE__) . '/../vendor/simpletest/reporter.php'; require_once dirname(__FILE__) . '/Test.php'; require_once dirname(__FILE__) . '/UnitTestCase.php'; -error_reporting(E_ALL); +error_reporting(E_ALL | E_STRICT); $test = new GroupTest('Doctrine Framework Unit Tests'); @@ -141,9 +141,9 @@ $test->addTestCase(new Doctrine_Expression_Mssql_TestCase()); $test->addTestCase(new Doctrine_Expression_Pgsql_TestCase()); $test->addTestCase(new Doctrine_Expression_Oracle_TestCase()); $test->addTestCase(new Doctrine_Expression_Sqlite_TestCase()); - + */ // Core - */ + /** */ $test->addTestCase(new Doctrine_Access_TestCase()); //$test->addTestCase(new Doctrine_Configurable_TestCase()); @@ -233,6 +233,7 @@ $test->addTestCase(new Doctrine_Query_Check_TestCase()); $test->addTestCase(new Doctrine_Query_Limit_TestCase()); + $test->addTestCase(new Doctrine_Query_IdentifierQuoting_TestCase()); $test->addTestCase(new Doctrine_Query_Update_TestCase()); $test->addTestCase(new Doctrine_Query_Delete_TestCase()); @@ -271,6 +272,8 @@ $test->addTestCase(new Doctrine_Collection_Snapshot_TestCase()); $test->addTestCase(new Doctrine_Hydrate_FetchMode_TestCase()); +//$test->addTestCase(new Doctrine_AuditLog_TestCase()); + // Cache tests //$test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());