I tried the following but did not solve it, as I dont know how to merge the trees
$roots=Category::model()->roots()->findAll();
foreach($roots as $root) {
$tree = Category::model()->findAll(
array(
'condition'=>'root='.$root->id,
'order'=>'lft'
)
);
}
// I need to merge all the trees, but don't know how
$this->renderPartial(
'//partials/category/_gridViewCategory',
array(
'tree'=>$tree,
)
);
I was thinking of using this extension but I have few questions about concepts made here. I am not very familiar with nested set using left-right-parent attributes (I did use pattern with extra table containing all relations), so my questions may be silly or my concepts are wrong. In such case simply point me such cases
Why not using events (beforeSave in this case) to apply specific functionalities and instead providing additional functions like ‘saveNode’? I like extensions that do not need any modifications to standard approach when it is not really needed - this is the reason I ask about it. Is there any reason for this extra interface methods?
Why there is additional attribute ‘root’ that is used in multi-root scenario? why can’t you just get roots by ‘id_parent = NULL’ or ‘level = 0’?
could you be more specific? What is default ‘save’ used for now?
if I find elements with level=0 or id_parent=null I can easily sort them by ‘lft’ and get 8th element of such list, or I can find interesting root with more specific query (by other columns). Consider such data:
Similar to the curiosity raised in this comment on this thread, I’d also be happy to know why there is the ‘root’ and ‘level’ columns in the schema. AFAIK, they are not strictly needed in nested set implementation. Is that for performance enhancement?
I’ve looked at the couple of resources linked in the mentioned comment: the first is a 404 now and the second doesn’t answer the question as well (though I’ve skimmed it).
Replying on my own post for the sake of others after getting half the answer here:
‘root’ column is a sort of foreign key, but to the same table, and it is used to relate the record to its root record in the tree represented in the relational table.
My thoughts: probably for performance reasons, avoiding traversing the entire tree with multiple queries, in case it is desired and all we have in our hands is a leaf down its structure.
To the best of my knowledge what you’re doing (above) is the only way to do it.
Nested set is a way to store a single tree information in SQL tables. The fact that the behavior class supports several trees in the same table is a nice add on, but nested set in theory has nothing to do with ordering of several roots.
I guess that going with what you already did or adding your own logic for ordering of roots is the way to go and there’s no support for that in this extension, or from the the theoretical point of view.
I need/want to move child BA and BB to upper level, meaning to be children of ROOT and to delete child B afterwards.
My problem is with the first two ‘moves’. I cannot seem to move them since I use $childBA->moveAfter($ROOT) and this throws an exception telling me that “The target node should not be root”. I also took a look at insertAfter() but this method is for new records only, yet child BA and child BB already exists.
How do I accomplish this?
Thanks!
EDIT: apparently, my usage was incorrect. moveAfter() requires the first parameter, $target, to be the destined brother to the node we’re moving, not its destined parent.
Indeed I’m unable to use moveAsLast() (though I didn’t really test it) since it will fail with the mentioned error, but using moveAfter() is possible and working.
I have a similar question as the one mentioned above, basically I have 2 models, categories and products. In category model I use nested set behavior to generate the categories tree.
$categoryModel = ErpProductCategory::model()->findByPk($id);
$model = new ErpProduct('search');
$model->unsetAttributes(); // clear any default values
if (isset($_POST['ErpProduct']))
{
$model->attributes=$_POST['ErpProduct'];
}
// get descendants of current category
$criteria = $categoryModel->descendants()->getDbCriteria();
// get current model alias
$alias = $model->getTableAlias();
// add conditions for category
$model->getDbCriteria()->mergeWith($criteria);
// also get the products directly under the category, not only those from subcategories
$model->getDbCriteria()->addCondition($categoryModel->getTableAlias().'.id = ' . $categoryModel->id, 'OR');
// restore the correct alias
$model->getDbCriteria()->alias = $alias;
$this->render('index', array(
'dataProvider' => $model->search(),
'categoryModel' => $categoryModel,
));
The resulting query would be something along the lines (I stripped a lot of code since I’m doing a more complex query with status checks for products and parent categories):
SELECT `erpp`.*, `erppc`.*
FROM `erp_product` `erpp`
LEFT OUTER JOIN `erp_product_category` `erppc` ON (`erpp`.`category_id`=`erppc`.`id`)
WHERE (((`erppc`.`lft`>6 AND `erppc`.`rgt`<9) AND (`erppc`.`root`=:ycp1)) OR (erppc.id = 3)) ORDER BY `erppc`.`lft` LIMIT 10. Bound with :ycp1='1'