更改父级或删除时更新嵌套集

Dug*_*ugi 6 php mysql database nested-sets sql-update

我有以下表结构,为方便起见,它也在sqlfiddle上:

+---------+-----------+---------+----------+-----------+-------------------+-----------------------------------+--------+--------------+-------------+---------------+-----------+
| rule_id | parent_id | left_id | right_id | rule_type | rule_title        | rule_description                  | public | parse_bbcode | parse_links | parse_smilies | group_ids |
+---------+-----------+---------+----------+-----------+-------------------+-----------------------------------+--------+--------------+-------------+---------------+-----------+
|       1 |         0 |       1 |        6 | cat       | Sample Category 1 |                                   |      1 |            0 |           0 |             0 | 1 2 7     |
|       2 |         1 |       2 |        3 | rule      | Sample Rule 1     | This is a sample rule description |      1 |            1 |           1 |             1 | 1 2 7     |
|       3 |         0 |       7 |        8 | cat       | Sample category 2 |                                   |      1 |            0 |           0 |             0 | 1 7 2     |
|       4 |         0 |       9 |       10 | cat       | Sample category 3 |                                   |      1 |            0 |           0 |             0 | 1 7 2     |
|       5 |         1 |       4 |        5 | rule      | Sample rule 3     | lol                               |      1 |            1 |           1 |             1 | 1 2 7     |
+---------+-----------+---------+----------+-----------+-------------------+-----------------------------------+--------+--------------+-------------+---------------+-----------+

如你所见,rule_type可以是'cat''rule'.

cat代表类别,类别是根节点:所以parent_id总是如此0.在我的代码中,我们可以通过检查if rule_type = 'cat'或来识别类别parent_id = 0.

您还可以看到我正在为我的项目使用嵌套集,这就是问题所在.

我已经成功创建了以下功能:

  • 上下移动规则和类别; 和

  • 将新规则或类别放在各自的位置.

但我不能设置规则" right_idleft_id如果我们改变它parent_id!我也不能设置right_idleft_id,如果我们删除一个规则或类别.

我将尝试用一个例子来解释.请注意,这只是一个示例,而不是实际情况,我需要一般性答案.

从上表中,我们看到我们有3个类别rule_id IN (1, 3, 4)和两个规则rule_id IN (2, 5).

规则rule_id = 2是与该类别的一部分rule_id = 1,我们可以看到,从parent_id列.如果我将其更改parent_id为4怎么办?将如何right_idleft_id被置使一切都准备好再次?我知道我们需要更新两个rule_id IN (1, 4)以重新排序所有内容,但我不知道我的查询是什么样的.

同样适用于删除...例如我删除rule_id = 2(这是一个规则),我将如何设置right_id&left_idparent_id = 1正确的顺序?或者当我删除一个类别?我该如何重新排序类别?

我没有真正尝试在这里做任何事情,因为我没有视力如何做这样的事情,所以我要求你的帮助,伙计们.

我希望我清楚自己.如果没有,请告诉我,我会尝试更具描述性.

Vad*_*man 7

我假设您已成功设置PDO连接.

此外,请注意以下示例仅在所有类别都是根节点(如主题中)时才起作用.更改此代码以使用嵌套类别不是问题.

删除规则

  1. 检索它的值right_idleft_id值.

  2. 从数据库中删除行.

  3. 更新表集right_id- 2,其中right_id大于right_id删除规则.

  4. 同样的 left_id

例:

    $ruleIdForDel = 2;
    $leftId = 2;
    $rightId = 3;

    $pdo->beginTransaction();
    try {
        $pdo->exec("DELETE FROM rules WHERE rule_id = $ruleIdForDel");
        $pdo->exec("UPDATE rules
                    SET left_id = CASE
                            WHEN left_id > $leftId THEN left_id - 2
                            ELSE left_id
                        END,
                        right_id = CASE
                            WHEN right_id > $rightId THEN right_id - 2
                            ELSE right_id
                        END");
        $pdo->commit();
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
Run Code Online (Sandbox Code Playgroud)

更新规则 parent_id

假设我们要将节点移动到新父节点的最后位置

  1. 检索更新规则left_idright_id($ruleLeftId$ruleRightId)

  2. 检索新的父规则left_idright_id($newParentRuleLeftId$newParentRuleRightId)

  3. 检查节点在树上向上或向下移动,并根据它生成更新规则的新值left_idright_id值($ruleNewLeftId$ruleNewRightId)

  4. 更新规则left_idright_id根据更新规则的目标

  5. 更新parent_id,left_id,right_id更新规则

如果更新规则right_key少于新的父规则,right_id则规则向下移动树,否则它向上移动树.

如果规则向下移动树,我们将left_id在更新规则left_id($ruleLeftId)和new left_id($ruleNewLeftId)+ 1 之间的规则上将值减去负2.否则,将新left_idleft_id($ ruleNewLeftId)之间的规则移位2 并且initial left_id($ruleLeftId).

同样的right_id.

例:

    // Updating rule
    $ruleId = 2;
    $ruleLeftId = 2;
    $ruleRightId = 3;

    // New parent rule
    $newParentRuleId = 3;
    $newParentRuleLeftId = 7;
    $newParentRuleRightId = 8;


    // Generate new rule's left and right keys
    // Moves up
    if ($newParentRuleRightId < $ruleRightId) {
        $ruleNewLeftId = $newParentRuleRightId;
        $ruleNewRightId = $newParentRuleRightId + 1;
    // Moves down
    } else {
        $ruleNewLeftId = $newParentRuleRightId - 2; // 6
        $ruleNewRightId = $newParentRuleRightId - 1; // 7
    }

    $pdo->beginTransaction();
    try {
        $pdo->exec("UPDATE rules
                            SET left_id = CASE
                                /* Moves down */
                                WHEN $ruleNewRightId > $ruleRightId AND
                                     left_id > $ruleLeftId AND
                                     left_id <= $ruleNewLeftId + 1 THEN left_id - 2
                                /* Moves up */
                                WHEN $ruleNewRightId < $ruleRightId AND
                                     left_id >= $ruleNewLeftId AND
                                     left_id < $ruleLeftId THEN left_id + 2
                                ELSE left_id
                            END,
                            right_id = CASE
                                WHEN $ruleNewRightId > $ruleRightId AND
                                     right_id > $ruleRightId AND
                                     right_id <= $ruleNewRightId THEN right_id - 2
                                WHEN $ruleNewRightId < $ruleRightId AND
                                     right_id >= $ruleNewLeftId AND
                                     right_id <= $ruleRightId THEN right_id + 2
                                ELSE right_id
                            END");
        $pdo->exec("UPDATE rules
                    SET parent_id = $newParentRuleId,
                        left_id = $ruleNewLeftId,
                        right_id = $ruleNewRightId
                    WHERE rule_id = $ruleId");
        $pdo->commit();
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
Run Code Online (Sandbox Code Playgroud)

我没有使用任何PDO::Statement只是为了节省空间.

我没有正确测试它,所以如果你找到一些问题就发布一个问题.