Skip to content

Instantly share code, notes, and snippets.

@ptheofan
Last active August 22, 2021 08:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ptheofan/d6760ebbaf2371c75c62 to your computer and use it in GitHub Desktop.
Save ptheofan/d6760ebbaf2371c75c62 to your computer and use it in GitHub Desktop.
Yii2 NestedSets class trait as a nice helper to use with any class that uses the NestedSets behavior
<?php
/**
* NestedSets in SQL - How to export a nested set subtree or tree into Yii2 Menu widget and as
* JsTree valid DataSet (nested format).
*/
class Category extends \yii\db\ActiveRecord
{
/**
* @return array
*/
public function behaviors()
{
$rVal = parent::behaviors();
$rVal[] = [
// @link https://github.com/creocoder/yii2-nested-sets
'class' => NestedSetsBehavior::className(),
];
return $rVal;
}
/**
* @return array
*/
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_ALL,
];
}
/**
* Convert a tree into nested arrays. If you use the default function parameters you get
* a set compatible with Yii2 Menu widget.
*
* @param int $depth
* @param string $itemsKey
* @param callable|null $getDataCallback
* @return array
*/
public function toNestedArray($depth = null, $itemsKey = 'items', $getDataCallback = null)
{
/** @var Category $nodes */
$nodes = $this->children($depth)->all();
$exportedAttributes = array_diff(array_keys($this->attributes), ['lft', 'rgt']);
$trees = [];
$stack = [];
foreach ($nodes as $node) {
if ($getDataCallback) {
$item = call_user_func($getDataCallback, $node);
} else {
$item = $node->toArray($exportedAttributes);
$item['url'] = [UrlRuleCategory::ROUTE, 'slug' => $item['slug']];
}
$item[$itemsKey] = [];
$l = count($stack);
while ($l > 0 && $stack[$l - 1]['depth'] >= $item['depth']) {
array_pop($stack);
$l--;
}
if ($l == 0) {
// Assign root node
$i = count($trees);
$trees[$i] = $item;
$stack[] = &$trees[$i];
} else {
// Add node to parent
$i = count($stack[$l - 1][$itemsKey]);
$stack[$l - 1][$itemsKey][$i] = $item;
$stack[] = &$stack[$l - 1][$itemsKey][$i];
}
}
return $trees;
}
/**
* Export NestedSets tree into JsTree nested format data
* @return array
*/
public static function asJsTree()
{
$rVal = [];
/** @var Category[] $roots */
$roots = static::find()->roots()->all();
foreach ($roots as $root) {
$rVal[] = [
'id' => $root->id,
'text' => $root->label,
'children' => $root->toNestedArray(null, 'children', function($node) {
return [
'id' => $node->id,
'text' => $node->label,
];
}),
];
}
return $rVal;
}
}
@nadirvishun
Copy link

Thanks for this example.
Undefined index: depth in this line: while ($l > 0 && $stack[$l - 1]['depth'] >= $item['depth']) {
add depth in asJsTree():

...
                'children' => $root->toNestedArray(null, 'children', function($node) {
                    return [
                        'id' => $node->id,
                        'text' => $node->label,
                        'depth' => $node->depth,//add
                    ];
                }),
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment