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

时间:2012-10-22 17:32:52

标签: php mysql database sql-update nested-sets

我有以下表结构,为方便起见also on 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。在我的代码中,我们可以通过选中rule_type = 'cat'还是parent_id = 0来确定类别。

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

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

  • 移动规则&类别向上或向下;以及

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

但我未能设置 RULES' right_id& left_id如果我们更改其parent_id!我也未能设置right_id& left_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_id& left_id设置好所有内容再次到位?我知道我们需要更新rule_id IN (1, 4)以重新排序所有内容,但我不知道我的查询会是什么样子。

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

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

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

2 个答案:

答案 0 :(得分:7)

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

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

删除规则

  1. 检索其right_idleft_id值。

  2. 从数据库中删除行。

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

  4. left_id

  5. 相同

    示例:

        $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;
        }
    

    更新规则的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_idleft_idright_id

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

      如果规则向下移动树,我们将在更新规则的left_idleft_id)和新{{1}之间的规则上将$ruleLeftId值减去2 }(left_id)+ 1.否则,将新$ruleNewLeftId($ ruleNewLeftId)与初始left_idleft_id之间的规则的left_id值改为2 )。

      $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; } 来节省空间。

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

答案 1 :(得分:1)

  

rule_id = 2的规则是rule_id = 1的类别的一部分,我们   可以从parent_id列中看到。如果我更改parent_id怎么办?   到4? right_id&amp; left_id得到设置所以一切都在   再来一次?我知道我们需要将rule_id IN(1,4)更新为   重新排序所有内容,但我不知道我的查询会是什么样子。

尝试这样的事情,我还没有测试过,但它可能会让你走上正确的轨道

UPDATE table SET parent_id = 4, 
                 right_id = (CASE WHEN parent_id = 4 THEN 1 END), 
                 left_id = (CASE WHEN parent_id = 4 THEN 2 END)