【问题标题】:Nested dataset structure in PHPPHP中的嵌套数据集结构
【发布时间】:2023-04-05 22:39:01
【问题描述】:

我正在尝试在 PHP (another description of the structure) 中实现 nested dataset structure 的面向对象版本。我已经创建了一个节点实现:

class Node
{
    private $parent;
    private $nodes = [];
    private $level = 1;
    private $left = 1;
    private $right = 2;

    /**
     * @return self|null
     */
    public function getParent()
    {
        return $this->parent;
    }

    private function setParent(self $parent = null)
    {
        $this->parent = $parent;
    }

    public function getLevel(): int
    {
        return $this->level;
    }

    private function setLevel(int $level)
    {
        $this->level = $level;
    }

    public function getLeft(): int
    {
        return $this->left;
    }

    private function setLeft(int $left)
    {
        $this->left = $left;
    }

    public function getRight(): int
    {
        return $this->right;
    }

    private function setRight(int $right)
    {
        $this->right = $right;
    }

    /**
     * @return static[]
     */
    public function getNodes(): array
    {
        return $this->nodes;
    }

    public function addNode(Node $new)
    {
        $new->setLevel($this->getLevel() + 1);
        $this->nodes[] = $new;

        // @todo
    }
}

但是我需要帮助实现addNode 方法,它应该向当前节点添加一个新节点并更新整个树,尤其是新添加的节点、父节点、子节点等.

为了让一切变得更简单,我创建了简单的测试用例,它将检查一切是否已正确实施:

$country = new Node();
$state = new Node();
$city = new Node();
$country->addNode($state);
$state->addNode($city);

assert($country->getLeft() === 1);
assert($country->getRight() === 6);
assert($country->getLevel() === 1);

assert($state->getLeft() === 2);
assert($state->getRight() === 5);
assert($state->getLevel() === 2);

assert($city->getLeft() === 3);
assert($city->getRight() === 4);
assert($city->getLevel() === 3);

【问题讨论】:

    标签: php oop php-7 recurrence


    【解决方案1】:

    在休息了一天,喝了杯咖啡后,终于成功完成了这项工作。这并不像看起来那么容易,因为添加新节点时必须重写整个树:

    public function addChild(Node $new): void
    {
        $this->nodes[] = $new;
        $new->setParent($this);
        $new->setLevel($this->getLevel() + 1);
        $new->setLeft($this->getLeft() + 1);
        $new->setRight($this->getLeft() + 2);
    
        $rootNode = $this;
        while (!empty($rootNode->getParent())) {
            $rootNode = $this->getParent();
        }
    
        $rootNode->setLeft(1);
        $this->updateTree($rootNode);
    }
    
    private function updateTree(Node $node): void
    {
        $startIndex = $node->getLeft();
        foreach ($node->getChildren() as $child) {
            $child->setLeft(++$startIndex);
            $child->setRight(++$startIndex);
    
            if (count($child->getChildren())) {
                $this->updateTree($child);
                $startIndex = $this->getLastChild($child)->getRight();
            }
        }
    
        $node->setRight($this->getLastChild($node)->getRight() + 1);
    }
    
    private function getLastChild(Node $node): Node
    {
        return $node->getChildren()[count($node->getChildren()) - 1];
    }
    

    该课程即将在 GitHub 上发布。

    【讨论】:

      【解决方案2】:

      您不应该定义父级或对它的引用,因为您可能最终会陷入引用循环,这在代码中不是一件坏事,但在设计中可能最终使您的代码无法理解什么时候发生的事情出错了。

      至于addNode函数,你似乎走对了,

      public function addNode(Node $new)
      {
          //no need for getters and setters from inside the class
          $new->level = $this->level + 1;
          $this->nodes[] = $new;
          return $this;
      }
      

      我不确定你想用leftright 做什么。但我认为它的顶部父母一直在左边,而底部的孩子一直在右边。在那种情况下,我只是将它与关卡区分开来。

      我也不会使用 private 而是使用 protected,我在这里主要是这样做的,所以我可以一直向上走,而不需要 getter 和 setter 来使代码更紧凑,但是你可以按照你的感觉去做:

      class Node
      {
      
          /**
           * @var static
           */
          protected $parent;
          /**
           * @var static[] 
           */
          protected $nodes = [];
          /**
           * @var int 
           */
          protected $level = 1;
          /**
           * @var int 
           */
          protected $left = 0;
          /**
           * @var int 
           */
          protected $right = 0;
      
      
          public function addNode(Node $new)
          {
              $new->level = $this->level + 1;
              $new->parent = $this;
              $new->left = $new->level - 1;
              if (empty($this->nodes)) {
                  $this->right = 1;
              }
              $curr = $this;
              while (null !== $curr->parent) {
                  //walking up to the current parent and telling it there is a new level added
                  $curr->parent->right++;
                  //setting the current level 1 up
                  $curr = $curr->parent;
              }
      
              $this->nodes[] = $new;
              return $this;
          }
      }
      

      因为我返回$this,所以我可以链接所有内容,或者嵌套 addNode 调用,如

      $country = new Node();
      $state = new Node();
      $city = new Node();
      $country->addNode($state->addNode($city));
      

      【讨论】:

      • 断言没有通过。 leftright 是为了在树被持久化后轻松查询 MySQL 数据库中的结构。
      • 你的权利,我知道他们不会,因为权利考虑到右边还有多少人,而不仅仅是6 - level,这很容易做到,我不会解决你的代码你,只是帮助你学习如何自己做。