Php 两表邻接表模型

Php 两表邻接表模型,php,zend-framework,tree,hierarchical-data,adjacency-list-model,Php,Zend Framework,Tree,Hierarchical Data,Adjacency List Model,因此,我认为我的问题归结为两个问题: 当树存储在MySQL(两个表之间)中时,如何使用邻接列表模型方法在PHP中构建可遍历的树结构,同时考虑性能 以所需格式显示树的可维护方法是什么,而不必重复遍历代码,也不必使用if/else和switch语句来乱丢逻辑 详情如下: 我正在使用Zend框架 我正在做一份调查问卷。它存储在一个MySQL数据库中,位于两个单独的表之间:问句和问句组。每个表都扩展了相应的Zend_Db_table_*类。层次结构使用邻接列表模型方法表示 我意识到我遇到的问题很可能是因

因此,我认为我的问题归结为两个问题:

  • 当树存储在MySQL(两个表之间)中时,如何使用邻接列表模型方法在PHP中构建可遍历的树结构,同时考虑性能

  • 以所需格式显示树的可维护方法是什么,而不必重复遍历代码,也不必使用if/else和switch语句来乱丢逻辑

  • 详情如下:

    我正在使用Zend框架

    我正在做一份调查问卷。它存储在一个MySQL数据库中,位于两个单独的表之间:问句和问句组。每个表都扩展了相应的Zend_Db_table_*类。层次结构使用邻接列表模型方法表示

    我意识到我遇到的问题很可能是因为我正在将树结构填充到RDBMS中,所以我对替代方案持开放态度。不过,我还存储了问卷调查对象及其回答,因此需要其他方法来支持这一点

    问卷需要以各种HTML格式显示:

  • 作为输入响应的表单(使用Zend_表单)
  • 作为有序列表(嵌套),以问题(和某些组)作为链接,按问题或组查看回答
  • 作为一个有序列表(嵌套),每个问题都附有答案
  • 问题是叶节点,问题组可以包含其他问题组和/或问题。总共有100多行要处理和显示

    目前,我有一个视图助手,它使用递归进行所有处理,以检索问题组的子项(一个在两个表之间执行联合的查询:QuestionGroup::getChildren($id))。另外,当显示带有问题回答的问卷时,还需要另外两个查询来检索受访者及其对每个问题的回答

    虽然页面加载时间不是很长,但这种方法感觉是错误的。递归加上对几乎每个节点的多个数据库查询,并不会让我感到内心的温暖和模糊

    我已经在从UNION返回的完整树数组上尝试了一些递归方法,以构建一个分层数组来遍历和显示。但是,由于组和问题存储在单独的表中,因此存在重复的节点ID,因此这种情况似乎无法解决。也许我错过了一些东西

    目前,以上面列出的格式显示树的逻辑相当混乱。我不想到处重复遍历逻辑。然而,各地的条件语句也不能生成最容易维护的代码。我已经阅读了Visitor、Decorator和一些PHP SPL迭代器,但我仍然不清楚它们如何与扩展Zend_Db_Table、Zend_Db_Table和Zend_Db_Table的类一起工作。特别是因为我还没有解决之前从数据库构建层次结构的问题。添加新的显示格式(或修改现有格式)会很容易。

    • 邻接列表通常在每一行中为您提供一个
      parent\u id
      列,该列将一行链接到其直接父行。如果行是树的根,
      parent\u id
      为空。但这会导致您运行许多SQL查询,这非常昂贵

    • 添加另一列
      root\u id
      ,这样每一行都知道它所属的树。这样,您就可以通过一个SQL查询获取给定树的所有节点。在
      类中添加一个方法,通过树的根id获取
      行集

      class QuestionGroups extends Zend_Db_Table_Abstract
      {
          protected $_rowClass = 'QuestionGroup';
          protected $_rowsetClass = 'QuestionGroupSet';
          protected function fetchTreeByRootId($root_id)
          {
               $rowset = $this->fetchAll($this
                  ->select()
                  ->where('root_id = ?', $root_id)
                  ->order('id');
              );
              $rowset->initTree();
              return $rowset;
          }
      }
      
    • 编写一个扩展
      Zend\u Db\u Table\u Row
      的自定义类,并编写函数来检索给定行的父行及其子行的
      Rowset
      类应包含受保护的数据对象,以引用父对象和子对象数组。
      对象还可以具有用于面包屑的
      getLevel()
      函数和
      getAncestorsRowset()
      函数

      class QuestionGroup extends Zend_Db_Table_Row_Abstract
      {
          protected $_children = array();
          protected $_parent   = null;
          protected $_level    = null;
          public function setParent(Zend_Db_Table_Row_Abstract $parent)
          {
              $this->_parent = $parent;
          }
          public function getParent()
          {
              return $this->_parent;
          }
          public function addChild(Zend_Db_Table_Row_Abstract $child)
          {
              $this->_children[] = $child;
          }
          public function getChildren()
          {
              return $this->_children;
          }
          public function getLevel() {}
          public function getAncestors() {}
      }
      
    • 编写一个自定义类扩展
      Zend_Db_Table_Rowset
      ,该类具有迭代行集中的行的函数,设置父引用和子引用,以便随后可以将它们作为树进行遍历。另外,
      Rowset
      应该有一个
      getRootRow()
      函数

      class QuestionGroupSet extends Zend_Db_Table_Rowset_Abstract
      {
          protected $_root = null;
          protected function getRootRow()
          {
              return $this->_root;
          }
          public function initTree()
          {
              $rows = array();
              $children = array();
              foreach ($this as $row) {
                $rows[$row->id] = $row;
                if ($row->parent_id) {
                  $row->setParent($rows[$row->parent_id]);
                  $rows[$row->parent_id]->addChild($row);
                } else {
                  $this->_root = $row;
                }
              }
          }
      }
      

    现在可以对行集调用
    getRootRow()
    ,它将返回根节点。拥有根节点后,可以调用
    getChildren()
    并在其上循环。然后,您也可以对这些中间子级中的任何一个调用
    getChildren()
    ,并以任意格式递归输出一个树。

    谢谢,比尔。我在这两个表中都添加了一个root_id列,并实现了您建议的方法()。但我看不到全局。遍历行集中的行的方法如何处理分布在两个表中的树?我假设fetchTreeByRootId()需要使用联合。或者创建视图是更好的解决方案?虽然我很喜欢能够利用现有的课程…比尔,谢谢你的澄清。它工作得很好。我仍在努力将叶子(问题)整合到组合中。完成后,我将发布我的最终解决方案。希望能让其他可能遇到同样情况的人受益。我很高兴这对你有用。顺便说一句,我将上面的
    initTree()
    更改为
    public
    函数,因为它必须由Table类调用。你可能已经找到了同样的东西。