laravel-nestedset icon indicating copy to clipboard operation
laravel-nestedset copied to clipboard

Multiple root nodes eager loaded tree

Open mtx-z opened this issue 4 years ago • 1 comments

Hello, first: thanks for this great package. Really cool eloquent and collection work here. My goal is to display a tree with multiple root nodes, not particularly node 0 or same level root. I can make it work, but it could be performance improved (eloquent).

My case

  • see the attached tree see the attached tree

  • My nestedset Category model belongsToMany User model (many to many with pivot)

  • My User is attached to node 1, 5 and 7 (attached tree)

  • I need a tree with 3 root nodes and their respective children & descendants. So a user has one or multiple "virtual" root node(s) (not especially node 0, nor same level nodes).

  • User should get a tree having:

    • 3 root nodes (1, 5 & 7) at tree top level
    • children and ancestors under node 1 and 5
    • nothing under root node 7

What is working

I can manage to get my multiple root node tree with their descendants with a foreach. But I can't get a eager loading of all descendants from all root nodes in one optimised query.

$categories = $user()->categories()->get(['categories.id']); //all node from given user
$nodes = collect([]);

foreach ($categories as $node) {
     $nodes = $nodes->merge(Category::descendantsAndSelf($node->id)->toTree());
}

This returns the correct tree:

  • Nodes 1, 5 & 7 as root nodes (multiple top level)
  • Nodes 1 & 5 have all their children and descendants
  • Node 7 is here as top level, without any children

What is not working

I tried multiple things to eager-load multiple nodes with their descendants.

$user_nodes = $user->categories()->pluck('id')->toArray(); //array of nodes ID
$tree = Category::whereIn('id', $user_nodes )->with('descendants');
$nodes = $tree->get()->toTree();

will return few of the root node, without any descendant

  • returns a tree with node 1 and 5 only, without descendants
  • missing node 7 (on my test database with more nodes, only the 3 first of 8 root node shows, without any descendants)
$nodes = Auth::user()->categories()->with('descendants');
$tree = $nodes->get()->toTree();

will return same result as before

few tests

$tree= Category::where('id', 1)->with('descendants')->get()->toTree(); Will only generate a tree with root Node 1, without any children (nodes 3 & 4) or ancestors (node 8), but there are some : $nodes = Category::descendantsAndSelf(1)->toTree(); returns a correct tree with node 1 as root and with children (nodes 3 & 4) and descendants (node 8).

what would be needed

  • Category::descendantsAndSelf() accept and array of node ID, and generate an eloquent request that load categories nodes and eager-load descendants (descendantsAndSelfves) (it seems complex to implement seeing namespace Kalnoy\Nestedset\QueryBuilder; whereDescendantOf() )
  • Auth::user()->categories()->with('descendants')->get()->toTree(); eloquent or collection trick to get the multi root nodes full tree

Maybe I'm missing something when starting from a classic eloquent query to get a full tree. My improvements research seems related to #388.

Any advice would be much appreciated!

Thank you for your attention and your help.

mtx-z avatar Sep 17 '19 21:09 mtx-z