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
} else {
$parent = $map['parent'];
$relation = $map['relation'];
if (!isset($prev[$parent])) {
break;
}
// check the type of the relation
if ( ! $relation->isOneToOne()) {
// initialize the collection
......@@ -1082,6 +1087,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable
$parent = $this->_aliasMap[$alias]['parent'];
$relation = $this->_aliasMap[$alias]['relation'];
$componentAlias = $relation->getAlias();
if (!isset($prev[$parent])) {
break;
}
// check the type of the relation
if ( ! $relation->isOneToOne()) {
......@@ -1154,6 +1163,10 @@ class Doctrine_Hydrate extends Doctrine_Object implements Serializable
} else {
$prev[$alias] = $coll->getLast();
}
} else {
if (isset($prev[$alias])) {
unset($prev[$alias]);
}
}
}
}
......
......@@ -50,6 +50,13 @@ class Doctrine_Node implements IteratorAggregate
* @param array $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
......@@ -61,6 +68,7 @@ class Doctrine_Node implements IteratorAggregate
{
$this->record = $record;
$this->options = $options;
$this->_tree = $this->record->getTable()->getTree();
}
/**
......
This diff is collapsed.
......@@ -31,6 +31,8 @@
*/
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
*
......@@ -55,11 +57,12 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
public function setTableDefinition()
{
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('rgt', 'integer', 11);
$this->table->setColumn('lft', 'integer', 4);
$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
$record->set('lft', '1');
$record->set('rgt', '2');
$record->set('level', 0);
$record->save();
......@@ -90,107 +94,110 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
* returns root node
*
* @return object $record instance of Doctrine_Record
* @deprecated Use fetchRoot()
*/
public function findRoot($rootId = 1)
{
$q = $this->table->createQuery();
$q = $q->where('lft = ?', 1);
return $this->fetchRoot($rootId);
}
/**
* 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
$q = $this->returnQueryWithRootId($q, $rootId);
$data = $q->execute();
$root = $q->execute()->getFirst();
// if no record is returned, create record
if ( ! $root) {
$root = $this->table->create();
if (count($data) <= 0) {
return false;
}
// set level to prevent additional query to determine level
$root->getNode()->setLevel(0);
if ($data instanceof Doctrine_Collection) {
$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;
}
/**
* optimised method to returns iterator for traversal of the entire tree from root
* Fetches a tree.
*
* @param array $options options
* @return object $iterator instance of Doctrine_Node_NestedSet_PreOrderIterator
* @param array $options Options
* @return mixed The tree or FALSE if the tree could not be found.
*/
public function fetchTree($options = array())
{
// fetch tree
$q = $this->table->createQuery();
$q = $this->getBaseQuery();
$componentName = $this->table->getComponentName();
$q = $q->where("$componentName.lft >= ?", 1)
->orderBy("$componentName.lft asc");
$q = $q->addWhere("base.lft >= ?", 1);
// if tree has many roots, then specify root id
$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();
$root = $tree->getFirst();
// 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);
if (count($tree) <= 0) {
return false;
}
// 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 array $options options
* @return iterator instance of Doctrine_Node_<Implementation>_PreOrderIterator
* @param mixed $pk primary key as used by table::find() to locate node to traverse tree from
* @param array $options Options.
* @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())
{
$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?
return false;
}
//$depth = isset($options['depth']) ? $options['depth'] : null;
if ($record->exists()) {
$options = array_merge(array('include_record'=>true), $options);
return $record->getNode()->traverse('Pre', $options);
}
$q = $this->getBaseQuery();
$params = array($record->get('lft'), $record->get('rgt'));
$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()
{
$q = $this->table->createQuery();
$q = $q->where('lft = ?', 1);
$q = $this->getBaseQuery();
$q = $q->where('base.lft = ?', 1);
return $q->execute();
}
......@@ -238,9 +245,98 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int
public function returnQueryWithRootId($query, $rootId = 1)
{
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;
}
/**
* 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
{
$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.date, a.username, vr.title, vr.color, vr.icon")
->from("QueryTest_Category c")
->leftJoin("c.boards b")
->leftJoin("b.lastEntry le")
......@@ -204,19 +204,13 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase
// get the baord for inspection
$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->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->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('red', $board['lastEntry']['author']['visibleRank']['color']);
$this->assertEqual('freak.png', $board['lastEntry']['author']['visibleRank']['icon']);
......@@ -255,8 +249,8 @@ class Doctrine_Query_OneToOneFetching_TestCase extends Doctrine_UnitTestCase
// get the board for inspection
$tmpBoard = $categories[0]['boards'][0];
$this->assertTrue( ! isset($board['lastEntry']));
$this->assertTrue( ! isset($tmpBoard['lastEntry']));
} catch (Doctrine_Exception $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