Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Nestedset change item order
  • Hi,

    I'm having a simple nested set (one tree only):
    - MAIN DUMMY
        - CHILD1
             - NESTED CHILD1
             - NESTED CHILD2
             - NESTED CHILD3
        - CHILD2
        [..]

    Is this possible to change the order of those? Eg. move child2 above child1? Or I would have to clean the whole current tree and create new nodes everytime?

    For front-end I'm using Javascript to set the order, so I got the array as follows
    (schema is: node-xx[id]=parent&[...]):

    node-0[79]=null&node-79[80]=79&node-79[81]=79&node-79[82]=79&node-79[83]=79&node-79[84]=79&node-0[85]=null&node-85[86]=85&node-85[87]=85&node-85[88]=85&node-85[89]=85&node-85[90]=85

    Now I only need to get the idea to save the order in the backend.
  • Yes, you can use the the next_sibling() / previous_sibling() methods for that.

    $child2->previous_sibling($child1)->save();

    will move child2 to before child1. This is the beauty of nested sets. ;-)

    The docs have a few examples of how to use these methods, all examples are related to the tree diagram that is shown at the top of the page, so you can see what moves where.
  • I think sibling methods wouldnt be efficient in my case.

    When I reorder I send the ajax request to the rest controller containing whole tree array having following schema:

    $tree = [
    child_id => parent_id,
    child_id => parent_id,
    another_child_id => parent_id,
    ];

    Detailed array dump: https://bin.fuelphp.com/~OR



    So I decided to use the last_child($parent) method, however it's still very slow. My tree contains 53 nodes (rows) and such update is executing more than 300 queries... insane.

    Current code:

    foreach($tree as $childrenId => $parentId)
    {
          $parent = Model_Category::find( (int)$parentId );

          if ( $parent )
         {
          $children = Model_Category::find( (int)$childrenId );
                    $children->last_child($parent)->save();
         }
    }

    Any idea how can I do this better? Thinking about performance. 
  • That can't be, the action of moving a node are three queries. Your code loops over the entire tree and moves or saves everything?

    Your ajax call should be something like POST /api/nodes/reorder/5/6

    which then in the backend
    - does a find(5)
    - does a find(6)
    - does a $six->previous_sibling($five) to swap positions

    which is a total of 5 queries, 2 of which are on primary key, 1 is an update of the pointers of the node being move, and 2 are the renumbering of the left- and right pointers of the nodes following the moved node to close the created hole.
  • Yes, currently my code (see my previous post) loops over the entire tree. Basically this is not only a swap-positions case. I display my tree structure in HTML and allow to drag & drop certain node at any place.


    Thats why I loop over the entire tree - I only have to serialize the tree in the Javascript and save as-it-is it in Fuel. 

    Otherwise I would have to detect where the node was dropped (I do have a trigger for that in Javascript) and now, this can become a mess - what to detect? what should I send through AJAX and finally, what methods will I have to use to save the node to the position where it was dropped?


    Can't see a simpler solution other than sending the entire tree data and just update the positions but that queries count... ugh.


  • I didn't describe a swap, I described the move of a node. Which is exactly what drag and drop does, we have plenty of apps that use that kind of operation, which the type of URL I used. We only pass two id's, you don't need more to move a node.

    And we do it de way I described it. previous_sibling() does "drop this node just in front of me", next_sibling() does "drop this node just behind me". It doesn't matter where the node comes from, the model will readjust the pointers of the tree. 

    If you use it to move a node "up" or "down" in the tree, all it's sub-nodes will move with it.

    So if you look at the picture on https://fuelphp.com/docs/packages/orm/model/nestedset.html, if you would do

    $kerry->previous_sibling($ben)->save();

    it would move Kerry up, in between Angela and Ben, and it would move Jim one level up, since it will remain a subordinate of Kerry.
  • "We only pass two id's"

    ... and those are? I'm assuming a previous node and current node.

    Like I said previous_sibling method doesn't work at all. 


    Lets say I want to move 'Item 2' after 'Item 9'.
    Our call would be /api/reorder/2/9
    Our code would be:

    public function action_reorder($previous, $current)
    {
    $previous = Model_Category::find((int) $previous);
    $current = Model_Category::find((int) $current);

    if ($previous && $current)
    {
    $current->previous_sibling($previous)->save();
    }
    }

    Is that correct? 
  • Which ones depend on your dropping mechanism. In general, we insert before, so we pass the id of the node to be moved, and the id of the node in front of which it should be dropped.

    You have to read the method call as

    "make $current the previous sibling of $previous"

    So if your action is "drop after" instead of "drop before", it should be

    public function action_reorder($tomove, $after)
    {
        $tomove = Model_Category::find( (int) $tomove);
        $after = Model_Category::find( (int) $after);

        if ($tomove and $after)
        {
            $tomove->next_sibling($after)->save();
        }
    }

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion