thank you very much, Samdark, i’m testing this right now…facing some troubles in rendering but have already posted in the EJNestedTreeActions forum topic
regards!!
![]()
thank you very much, Samdark, i’m testing this right now…facing some troubles in rendering but have already posted in the EJNestedTreeActions forum topic
regards!!
![]()
I am trying somoething very simple like…
$root=Categories::model()->roots()->findByPk(1);
$cat = new Categories();
$cat->name = 'Cool';
$root->append($cat);
$root->save();
// Does nothing too <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/sad.gif' class='bbc_emoticon' alt=':(' />
$cat->save();
Or…
$cat = new Categories();
$cat->name = 'Cool';
$cat->appendTo($root);
$cat->save();
But having no luck nothing get’s saved
Could you please add some documentation how to actually add / modify / delete nodes ![]()
Also please add to the documentation what values a root needs left: 1, right:0, root: (primary id) Else it would throw some sql errors…
Ah sorry… I had validation in my model on lft / rgt / lvl… But the behavior runs after validation.
And I notice the root needst left = 1 / rgt = 2 else it will move the root to child ![]()
Any reason why there is not a addRoot method ?
Also it be nice to have some convience method to have the tree return in an multi dimensional associative array… As far as I can see now descendants()->findAll() just returns all records in a flat array.
Also how to get the the path? Like I select a category but want to get the entire path all the way up, usefull for breadcrumbs and filtering in categories.
like…
SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;
Also I see now that my first error was because validation is called before the makeRoot function… maybe better to call this after the makeRoot so the model can still have validation rules. And not have to call validate = false
Ok i needed this badly so i added my own function… but if anybody can show how to do with activerecord plz do ![]()
/**
* Returns the path of the node
* @return array
*/
public function getPath()
{
$owner = $this->getOwner();
$alias = $owner->getTableAlias();
$pk = $owner->primaryKey;
$tbl_select = "";
$total = count($owner->tableSchema->columns);
$i = 0;
foreach($owner->tableSchema->columns as $column => $value){
$i++;
$tbl_select .= "parent.".$column;
if($i < $total){
$tbl_select .= ", ";
}
}
$sql = "SELECT $tbl_select
FROM ".$owner->tableSchema->name." AS node,
".$owner->tableSchema->name." AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.".$owner->tableSchema->primaryKey." = ".$pk."
AND parent.".$this->level." > 0 ";
if($this->hasManyRoots){
$sql .= "AND parent.".$this->root." = ".$owner->{$this->root}." ";
}
$sql .= " ORDER BY parent.lft;";
$command = Yii::app()->db->createCommand($sql);
$result = $command->queryAll();
return $result;
}
One more thing… I think tables now are not being locked when making modifications?
Or is the transaction enough?
Transactions should be enough but if you’ll get a problem with data, let me know.
Btw how to move the node to a new parent? I only see there is moveBefore / moveAsFirst and append does not seem to work ![]()
And why if I want to use the move methods there are restrictions that the node cannot be a descendant? If I want to move a child to top cannot ?
One more thing how to use the delete function? If just get the model instance and delete it will not be called… and will not remove the descendants. Shouldnt it use the beforeDelete event?
Because if i change it too:
/**
* Deletes node and it's descendants.
* @return boolean whether the deletion is successful.
*/
public function beforeDelete($event) {
parent::beforeDelete($event);
$owner=$this->getOwner();
if($owner->getIsNewRecord())
throw new CDbException(Yii::t('yiiext','The node cannot be deleted because it is new.'));
$transaction=$owner->getDbConnection()->beginTransaction();
try
{
$root=$this->hasManyRoots ? $owner->{$this->root} : null;
if($owner->isLeaf())
$result=$owner->delete();
else
{
$condition=$this->left.'>='.$owner->{$this->left}.' AND '.
$this->right.'<='.$owner->{$this->right};
if($root!==null)
$condition.=' AND '.$this->root.'='.$root;
$result=$owner->deleteAll($condition)>0;
}
if($result)
{
$first=$owner->{$this->right}+1;
$delta=$owner->{$this->left}-$owner->{$this->right}-1;
$this->shiftLeftRight($first,$delta,$root);
$transaction->commit();
return true;
}
}
catch(Exception $e)
{
$transaction->rollBack();
}
return false;
}
It will work ![]()
Latest version supports both moving to the same root and moving to another root. moveBefore / moveAsFirst should work fine.
For structure like this:
R
N1
N2
N3
You can’t move N1 to N2 or N3.
What error have you got?
Ok yes i see now it works
the naming confused me… i though this method id only to move and existing child to the first position ![]()
But it would be nicer if a parent could turn into a child… for example if I have a drag and drop tree it can go any direction ![]()
also… if i try to do: $save = $model->moveAsFirst($root); it seems to corrupt my table putting left and right in the negative
Hmm. Could you provide some code + DB values (4—5 nodes) before and after moving to root? It will help.
How can i create root node, when many roots mode is off?
Yeah, some more examples would be great. In order to create a root node (guess I need one? Even in "single root" mode?), I tried:
$root = new Menu();
// $root->lft = 1;
// $root->rgt = 2;
// $root->level = 1;
$root->text = 'root';
$root->saveNode( false );
Both with the commented attributes set and unset. I feel this implementation detail should be hidden, I didn’t even know if the counters start at zero or one until I read some comments in this thread, and maybe it is hidden and I don’t have to care about it. Just still didn’t find out.
In the end, this always throws an exception “Many roots mode is off”. And since it’s a new record and since I can’t save the AR using CActiveRecord::save, I’m feeling kind of lost here.
In the end, I created the root node directly in the DB (lft:1, rgt:2, level: 1, root: null) and managed to insert a node using the following snippet of code:
$root = Menu::model()->roots()->find();
if ($root instanceof CActiveRecord)
{
$help = new Menu();
$help->text = 'Help';
$appended = $help->appendTo( $root, false );
}
Maybe this helps others to get started.
Is there a chance to get some small code snippets for each of the provided API methods? For example it seems to me as if the saveNode method is useless in single root mode, but maybe I’m using it the wrong way.
Another quick snippet:
protected function createDefaultMenuItems()
{
$root = Menu::model()->roots()->find();
if (!$root instanceof CActiveRecord)
return;
$help = new Menu();
$help->text = 'Help';
$help->enabled = true;
$appended = $help->appendTo( $root, false );
$about = new Menu();
$about->text = 'About';
$about->enabled = true;
$about->appendTo( $help, false );
// --- test data - START --------------------------------------------------
$extensions = new Menu();
$extensions->text = 'Extensions';
$extensions->enabled = true;
$extensions->insertBefore( $help, false );
$install = new Menu();
$install->text = 'Install';
$install->enabled = false;
$install->appendTo( $extensions, false );
$uninstall = new Menu();
$uninstall->text = 'Uninstall';
$uninstall->enabled = false;
$uninstall->insertAfter( $install, false );
$separator = new Menu();
$separator->text = '-';
$separator->enabled = false;
$separator->insertAfter( $uninstall, false );
$hasChildren = new Menu();
$hasChildren->text = 'Has children';
$hasChildren->enabled = true;
$hasChildren->insertAfter( $separator, false );
$child1 = new Menu();
$child1->text = 'Child 1';
$child1->enabled = false;
$child1->appendTo( $hasChildren, false );
$child2 = new Menu();
$child2->text = 'Child 2';
$child2->enabled = false;
$child2->insertAfter( $child1, false );
// --- test data - END --------------------------------------------------
}
In words:
Use "appendTo( parent, false )" for the first child you add to a parent
Use "insertAfter( sibling, false )" for subsequent children you want to add to a group
I tried to build up my groups using:
$child1->appendTo( $parent, false );
$child2->appendTo( $parent, false );
before, but every appendTo inserted the item as first child of parent which wasn’t what I’d expected.
I’m trying to understand what values are stored in the ‘root’ field and the ‘level’ field. I wouldn’t need to know this except that I’m building a backoffice administration app that we’ll most likely use to configure our categories. I want to make sure I use the correct logic so that this extension will work if and when I want to use it.
Here is the test that will probably help you: http://code.google.com/p/yiiext/source/browse/trunk/app/tests/unit/ENestedSetBehaviorTest.php
Also there is an article: http://en.wikipedia.org/wiki/Nested_set_model
Hey
First of all, Thank you for implementing nested tree!
I’m facing a problem atm. The situation:
I’m making a webshop with nested categories, using the nested tree. When someone deletes a category, the program seeks for products assigned to that category and its subcategories, if found, the category must be not deleted. So I run the event beforeDelete. This works like a charm when the category is a leaf, but when it has subcategories the event doesn’t get raised because of deleteAll() used in nestedtree’s delete() method.
I think I will manually raise the event in the controller before deleting (if the category is not a leaf) and lean on the event’s return. What do you think? Anyone had a similar case?
Manually raising event is the proper solution. At least for now.