dp13668681869 2018-01-03 11:16
浏览 72

使用CakePHP 2.x Tree和jstree更改节点的位置

I have an application using CakePHP 2.x and jstree 3.2.1. I'm trying to figure out how it's possible to specify the position of a node when writing it to the database with CakePHP. The position itself comes from jstree...

When I drag and drop items with jstree the request URL gives me:

  • id - The ID of the node being dragged.
  • parent_id - The (parent) ID which the node has been dropped under.
  • position - this is an integer which starts at 0 and represents the position at which id has been dropped under parent_id. For example, a position of 2 means it should appear in 3rd position (3rd, not 2nd, because they start at 0).

CakePHP has methods in it's Tree Behaviour that allow you to move nodes in its Tree up and down. These methods are called moveUp() and moveDown() respectively.

I don't understand how it's possible to use the data provided from jstree with Cake's Tree behaviour such that you could update/save positions correctly.

Passing position to moveUp() or moveDown() would produce the wrong result. Why?

  • If jstree provides position = 2 and you were moving the 10th item in a list calling moveUp(2) through CakePHP means it would go into the 8th position, not the 2nd as intended. Similarly, moveDown(2) would move it to the 12th, which is not the desired outcome.

The schema that Cake has for it's Tree Behavior does not have a "position" field. Rather it uses lft and rght fields. The schema has:

  • id - ID of each individual tree node (auto increment)
  • parent_id - parent ID of the node. NULL if top level (no parent).
  • lft, rght - for MPTT logic. Cake generates these values automatically. They can be used to determine the order using ORDER BY lft ASC. But they are not the same values as position provided by jstree, and are unique for every row
  • name - text name of the node.

For example, consider the following tree:

  • D (id = 149)
    • 1 (id = 150, parent_id = 149)
    • 2 (id = 153, parent_id = 149)
    • 3 (id = 154, parent_id = 149)
    • 4 (id = 155, parent_id = 149)
    • 5 (id = 156, parent_id = 149)

In the database Cake stores this as follows:

enter image description here

If I was to use jstree to drag and drop "2" so it appears between "3" and "4" it would make a request containing the following GET variables:

id = 153. This is the ID of "2"

parent_id = 149. This is the ID of "D" which is the parent node of "2".

position = 2. This means the 3rd position (3rd because positions start from 0).

But I cannot regenerate the lft and rght values from this data? And moveUp()/moveDown() are not helpful here because position cannot be passed in a way that would make this work.

The tree should be in the following order. id and parent_id should not change, but lft and rght must, because everything under "D" has effectively been re-ordered:

  • D
    • 1
    • 3
    • 2 (moved)
    • 4
    • 5

Can anyone help with this?

  • 写回答

1条回答 默认 最新

  • douwei1904 2018-01-08 15:25
    关注

    I never used jsTree, but if it gives you the new parent ID and the sort position, then you should be able to use a combination of saving the new parent:

    $Model->save(array('parent_id' => $parentId));
    

    afterwards obtaining its child list, which will include the new child:

    $children = $Model->children($parentId, true, array('id'));
    

    and using the sort position to determine the delta to move the modified child:

    $childIds = Hash::extract($children, '{n}.Model.id');
    $positionMap = array_flip($childIds);
    
    $currentPosition = $positionMap[$nodeId];
    $delta = (int)$position - $currentPosition;
    
    if ($delta !== 0) {
        if ($delta < 0) {
            $Model->moveUp($nodeId, abs($delta));
        } else {
            $Model->moveDown($nodeId, abs($delta));
        }
    }
    

    This is just some rough example that should illustrate the idea of how this could work, it assumes $parentId to be the ID of the new parent, $nodeId to be the ID of the row being moved, and $position the (zero based) position the child was moved to. You'd have to account for other situations too, like for example when the parent ID doesn't change, ie only the sort position changes, and this should all be done in a transaction!

    Here's a complete snippet based on the Cookbook example, it will move the Gwendolyn node to the second position in the Work node:

    $nodeId = 8;
    $parentId = 9;
    $position = 1;
    
    $dataSource = $this->Category->getDataSource();
    $dataSource->begin();
    
    $this->Category->id = $nodeId;
    if (!$this->Category->save(array('parent_id' => $parentId))) {
        $dataSource->rollback();
    } else {
        $children = $this->Category->children($parentId, true, array('id'));
        $childIds = Hash::extract($children, '{n}.Category.id');
        $positionMap = array_flip($childIds);
    
        $result = true;
    
        if (isset($positionMap[$nodeId])) {
            $currentPosition = $positionMap[$nodeId];
            $delta = (int)$position - $currentPosition;
    
            if ($delta !== 0) {
                if ($delta < 0) {
                    $result = $this->Category->moveUp($nodeId, abs($delta));
                } else {
                    $result = $this->Category->moveDown($nodeId, abs($delta));
                }
            }
        }
    
        if ($result) {
            $dataSource->commit();
        } else {
            $dataSource->rollback();
        }
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序
  • ¥15 onvif+openssl,vs2022编译openssl64
  • ¥15 iOS 自定义输入法-第三方输入法
  • ¥15 很想要一个很好的答案或提示
  • ¥15 扫描项目中发现AndroidOS.Agent、Android/SmsThief.LI!tr
  • ¥15 怀疑手机被监控,请问怎么解决和防止