Commit 532d3da4 authored by romanb's avatar romanb

Bugfix for hydration. (zyne, please have a look at the diff).

Improvements and enhancements to the NestedSet (not BC! please have a look at draft/nestedset_changes.tree).
Added a model that was missing in the repos (model/BlogTag).
Updated a testcase.
parent abb77736
...@@ -1012,6 +1012,11 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable ...@@ -1012,6 +1012,11 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable
} else { } else {
$parent = $map['parent']; $parent = $map['parent'];
$relation = $map['relation']; $relation = $map['relation'];
if (!isset($prev[$parent])) {
break;
}
// check the type of the relation // check the type of the relation
if ( ! $relation->isOneToOne()) { if ( ! $relation->isOneToOne()) {
// initialize the collection // initialize the collection
...@@ -1082,6 +1087,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable ...@@ -1082,6 +1087,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable
$parent = $this->_aliasMap[$alias]['parent']; $parent = $this->_aliasMap[$alias]['parent'];
$relation = $this->_aliasMap[$alias]['relation']; $relation = $this->_aliasMap[$alias]['relation'];
$componentAlias = $relation->getAlias(); $componentAlias = $relation->getAlias();
if (!isset($prev[$parent])) {
break;
}
// check the type of the relation // check the type of the relation
if ( ! $relation->isOneToOne()) { if ( ! $relation->isOneToOne()) {
...@@ -1154,6 +1163,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable ...@@ -1154,6 +1163,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable
} else { } else {
$prev[$alias] = $coll->getLast(); $prev[$alias] = $coll->getLast();
} }
} else {
if (isset($prev[$alias])) {
unset($prev[$alias]);
}
} }
} }
} }
......
...@@ -50,6 +50,13 @@ class Doctrine_Node implements IteratorAggregate ...@@ -50,6 +50,13 @@ class Doctrine_Node implements IteratorAggregate
* @param array $iteratorOptions * @param array $iteratorOptions
*/ */
protected $iteratorOptions; protected $iteratorOptions;
/**
* The tree to which the node belongs.
*
* @var unknown_type
*/
protected $_tree;
/** /**
* contructor, creates node with reference to record and any options * contructor, creates node with reference to record and any options
...@@ -61,6 +68,7 @@ class Doctrine_Node implements IteratorAggregate ...@@ -61,6 +68,7 @@ class Doctrine_Node implements IteratorAggregate
{ {
$this->record = $record; $this->record = $record;
$this->options = $options; $this->options = $options;
$this->_tree = $this->record->getTable()->getTree();
} }
/** /**
......
This diff is collapsed.
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
*/ */
class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Interface class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Interface
{ {
private $_baseQuery;
/** /**
* constructor, creates tree with reference to table and sets default root options * constructor, creates tree with reference to table and sets default root options
* *
...@@ -55,11 +57,12 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int ...@@ -55,11 +57,12 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
public function setTableDefinition() public function setTableDefinition()
{ {
if ($root = $this->getAttribute('rootColumnName')) { if ($root = $this->getAttribute('rootColumnName')) {
$this->table->setColumn($root, 'integer', 11); $this->table->setColumn($root, 'integer', 4);
} }
$this->table->setColumn('lft', 'integer', 11); $this->table->setColumn('lft', 'integer', 4);
$this->table->setColumn('rgt', 'integer', 11); $this->table->setColumn('rgt', 'integer', 4);
$this->table->setColumn('level', 'integer', 2);
} }
/** /**
...@@ -80,6 +83,7 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int ...@@ -80,6 +83,7 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
$record->set('lft', '1'); $record->set('lft', '1');
$record->set('rgt', '2'); $record->set('rgt', '2');
$record->set('level', 0);
$record->save(); $record->save();
...@@ -90,107 +94,110 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int ...@@ -90,107 +94,110 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
* returns root node * returns root node
* *
* @return object $record instance of Doctrine_Record * @return object $record instance of Doctrine_Record
* @deprecated Use fetchRoot()
*/ */
public function findRoot($rootId = 1) public function findRoot($rootId = 1)
{ {
$q = $this->table->createQuery(); return $this->fetchRoot($rootId);
$q = $q->where('lft = ?', 1); }
/**
* Fetches a/the root node.
*
* @param integer $rootId
*/
public function fetchRoot($rootId = 1)
{
$q = $this->getBaseQuery();
$q = $q->where('base.lft = ?', 1);
// if tree has many roots, then specify root id // if tree has many roots, then specify root id
$q = $this->returnQueryWithRootId($q, $rootId); $q = $this->returnQueryWithRootId($q, $rootId);
$data = $q->execute();
$root = $q->execute()->getFirst(); if (count($data) <= 0) {
return false;
// if no record is returned, create record
if ( ! $root) {
$root = $this->table->create();
} }
// set level to prevent additional query to determine level if ($data instanceof Doctrine_Collection) {
$root->getNode()->setLevel(0); $root = $data->getFirst();
$root['level'] = 0;
} else if (is_array($data)) {
$root = array_shift($data);
$root['level'] = 0;
} else {
throw new Doctrine_Tree_Exception("Unexpected data structure returned.");
}
return $root; return $root;
} }
/** /**
* optimised method to returns iterator for traversal of the entire tree from root * Fetches a tree.
* *
* @param array $options options * @param array $options Options
* @return object $iterator instance of Doctrine_Node_NestedSet_PreOrderIterator * @return mixed The tree or FALSE if the tree could not be found.
*/ */
public function fetchTree($options = array()) public function fetchTree($options = array())
{ {
// fetch tree // fetch tree
$q = $this->table->createQuery(); $q = $this->getBaseQuery();
$componentName = $this->table->getComponentName(); $componentName = $this->table->getComponentName();
$q = $q->where("$componentName.lft >= ?", 1) $q = $q->addWhere("base.lft >= ?", 1);
->orderBy("$componentName.lft asc");
// if tree has many roots, then specify root id // if tree has many roots, then specify root id
$rootId = isset($options['root_id']) ? $options['root_id'] : '1'; $rootId = isset($options['root_id']) ? $options['root_id'] : '1';
$q = $this->returnQueryWithRootId($q, $rootId); if (is_array($rootId)) {
$q->orderBy("base." . $this->getAttribute('rootColumnName') . ", base.lft ASC");
} else {
$q->orderBy("base.lft ASC");
}
$q = $this->returnQueryWithRootId($q, $rootId);
$tree = $q->execute(); $tree = $q->execute();
$root = $tree->getFirst(); if (count($tree) <= 0) {
return false;
// if no record is returned, create record
if ( ! $root) {
$root = $this->table->create();
}
if ($root->exists()) {
// set level to prevent additional query
$root->getNode()->setLevel(0);
// default to include root node
$options = array_merge(array('include_record'=>true), $options);
// remove root node from collection if not required
if ($options['include_record'] == false) {
$tree->remove(0);
}
// set collection for iterator
$options['collection'] = $tree;
return $root->getNode()->traverse('Pre', $options);
} }
// TODO: no default return value or exception thrown? return $tree;
} }
/** /**
* optimised method that returns iterator for traversal of the tree from the given record primary key * Fetches a branch of a tree.
* *
* @param mixed $pk primary key as used by table::find() to locate node to traverse tree from * @param mixed $pk primary key as used by table::find() to locate node to traverse tree from
* @param array $options options * @param array $options Options.
* @return iterator instance of Doctrine_Node_<Implementation>_PreOrderIterator * @return mixed The branch or FALSE if the branch could not be found.
* @todo Only fetch the lft and rgt values of the initial record. more is not needed.
*/ */
public function fetchBranch($pk, $options = array()) public function fetchBranch($pk, $options = array())
{ {
$record = $this->table->find($pk); $record = $this->table->find($pk);
if ( ! ($record instanceof Doctrine_Record)) { if ( ! ($record instanceof Doctrine_Record) || !$record->exists()) {
// TODO: if record doesn't exist, throw exception or similar? // TODO: if record doesn't exist, throw exception or similar?
return false; return false;
} }
//$depth = isset($options['depth']) ? $options['depth'] : null;
if ($record->exists()) { $q = $this->getBaseQuery();
$options = array_merge(array('include_record'=>true), $options); $params = array($record->get('lft'), $record->get('rgt'));
return $record->getNode()->traverse('Pre', $options); $q->where("base.lft >= ? AND base.rgt <= ?", $params)->orderBy("base.lft asc");
} $q = $this->returnQueryWithRootId($q, $record->getNode()->getRootValue());
return $q->execute();
} }
/** /**
* fetch root nodes * Fetches all root nodes. If the tree has only one root this is the same as
* fetchRoot().
* *
* @return collection Doctrine_Collection * @return mixed The root nodes.
*/ */
public function fetchRoots() public function fetchRoots()
{ {
$q = $this->table->createQuery(); $q = $this->getBaseQuery();
$q = $q->where('lft = ?', 1); $q = $q->where('base.lft = ?', 1);
return $q->execute(); return $q->execute();
} }
...@@ -238,9 +245,98 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int ...@@ -238,9 +245,98 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
public function returnQueryWithRootId($query, $rootId = 1) public function returnQueryWithRootId($query, $rootId = 1)
{ {
if ($root = $this->getAttribute('rootColumnName')) { if ($root = $this->getAttribute('rootColumnName')) {
$query->addWhere($root . ' = ?', $rootId); if (is_array($rootId)) {
$query->addWhere($root . ' IN (' . implode(',', array_fill(0, count($rootId), '?')) . ')',
$rootId);
} else {
$query->addWhere($root . ' = ?', $rootId);
}
} }
return $query; return $query;
} }
/**
* Enter description here...
*
* @param array $options
* @return unknown
*/
public function getBaseQuery()
{
if (!isset($this->_baseQuery)) {
$this->_baseQuery = $this->_createBaseQuery();
}
return clone $this->_baseQuery;
}
/**
* Enter description here...
*
*/
private function _createBaseQuery()
{
$q = new Doctrine_Query();
$q->select("base.*")->from($this->table->getComponentName() . " base");
return $q;
}
/**
* Enter description here...
*
* @param Doctrine_Query $query
*/
public function setBaseQuery(Doctrine_Query $query)
{
$query->addSelect("base.lft, base.rgt, base.level");
if ($this->getAttribute('rootColumnName')) {
$query->addSelect("base." . $this->getAttribute('rootColumnName'));
}
$this->_baseQuery = $query;
}
/**
* Enter description here...
*
*/
public function resetBaseQuery()
{
$this->_baseQuery = null;
}
/**
* Enter description here...
*
* @param unknown_type $graph
*/
/*
public function computeLevels($tree)
{
$right = array();
$isArray = is_array($tree);
$rootColumnName = $this->getAttribute('rootColumnName');
for ($i = 0, $count = count($tree); $i < $count; $i++) {
if ($rootColumnName && $i > 0 && $tree[$i][$rootColumnName] != $tree[$i-1][$rootColumnName]) {
$right = array();
}
if (count($right) > 0) {
while (count($right) > 0 && $right[count($right)-1] < $tree[$i]['rgt']) {
//echo count($right);
array_pop($right);
}
}
if ($isArray) {
$tree[$i]['level'] = count($right);
} else {
$tree[$i]->getNode()->setLevel(count($right));
}
$right[] = $tree[$i]['rgt'];
}
return $tree;
}
*/
} }
<?php
class BlogTag extends Doctrine_Record
{
public function setUp() {
$this->hasMany('Photo', 'Phototag.photo_id');
}
public function setTableDefinition() {
$this->hasColumn('tag', 'string', 100);
}
}
...@@ -190,7 +190,7 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase ...@@ -190,7 +190,7 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase
{ {
$query = new Doctrine_Query($this->connection); $query = new Doctrine_Query($this->connection);
try { try {
$categories = $query->select("c.*, b.*, le.*, a.username, vr.title, vr.color, vr.icon") $categories = $query->select("c.*, b.*, le.date, a.username, vr.title, vr.color, vr.icon")
->from("QueryTest_Category c") ->from("QueryTest_Category c")
->leftJoin("c.boards b") ->leftJoin("c.boards b")
->leftJoin("b.lastEntry le") ->leftJoin("b.lastEntry le")
...@@ -204,19 +204,13 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase ...@@ -204,19 +204,13 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase
// get the baord for inspection // get the baord for inspection
$board = $categories[0]['boards'][0]; $board = $categories[0]['boards'][0];
// lastentry should've 2 fields. one regular field, one relation.
$this->assertEqual(2, count($board['lastEntry']));
$this->assertEqual(1234, (int)$board['lastEntry']['date']); $this->assertEqual(1234, (int)$board['lastEntry']['date']);
$this->assertTrue(isset($board['lastEntry']['author'])); $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('romanbb', $board['lastEntry']['author']['username']); $this->assertEqual('romanbb', $board['lastEntry']['author']['username']);
$this->assertTrue(isset($board['lastEntry']['author']['visibleRank'])); $this->assertTrue(isset($board['lastEntry']['author']['visibleRank']));
// visibleRank should've 3 regular fields
$this->assertEqual(3, count($board['lastEntry']['author']['visibleRank']));
$this->assertEqual('Freak', $board['lastEntry']['author']['visibleRank']['title']); $this->assertEqual('Freak', $board['lastEntry']['author']['visibleRank']['title']);
$this->assertEqual('red', $board['lastEntry']['author']['visibleRank']['color']); $this->assertEqual('red', $board['lastEntry']['author']['visibleRank']['color']);
$this->assertEqual('freak.png', $board['lastEntry']['author']['visibleRank']['icon']); $this->assertEqual('freak.png', $board['lastEntry']['author']['visibleRank']['icon']);
...@@ -255,8 +249,8 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase ...@@ -255,8 +249,8 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase
// get the board for inspection // get the board for inspection
$tmpBoard = $categories[0]['boards'][0]; $tmpBoard = $categories[0]['boards'][0];
$this->assertTrue( ! isset($board['lastEntry'])); $this->assertTrue( ! isset($tmpBoard['lastEntry']));
} catch (Doctrine_Exception $e) { } catch (Doctrine_Exception $e) {
print $e; print $e;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment