Pergunta

Então eu acho Meu problema se resume a duas perguntas:

  1. Como faço para construir uma estrutura de árvore travessa no PHP quando a árvore é armazenada no MySQL (entre duas tabelas) usando a abordagem do modelo de lista de adjacência, mantendo o desempenho em mente?

  2. Qual é uma abordagem sustentável para exibir a árvore nos formatos necessários sem duplicar o código de travessia e espalhar a lógica com as instruções IF/else e Switch?

Abaixo estão mais detalhes:

Estou usando a estrutura Zend.

Estou trabalhando com um questionário. Ele é armazenado em um banco de dados MySQL entre duas tabelas separadas: perguntas e question_groups. Cada tabela estende as classes zend_db_table_* apropriadas. A hierarquia é representada usando a abordagem do modelo da lista de adjacência.

Sei que os problemas em que estou enfrentando são provavelmente devido ao fato de estar enchendo uma estrutura de árvore em um RDBMS, por isso estou aberto a alternativas. No entanto, também estou armazenando os entrevistados de questionários e suas respostas para que abordagens alternativas precisem apoiar isso.

O questionário precisa ser exibido em vários formatos HTML:

  1. Como formulário para inserir respostas (usando Zend_Form)
  2. Como uma lista ordenada (aninhada) com perguntas (e algum grupos) como links para visualizar as respostas por pergunta ou por grupo.
  3. Como uma lista ordenada (aninhada) com respostas anexadas a cada pergunta.

As perguntas são nós foliares e o question_groups podem conter outros grupos de questões e/ou perguntas. Combinado, existem pouco mais de 100 linhas para processar e exibir.

Atualmente, tenho um ajudante de visualização que faz todo o processamento usando a recursão para recuperar os filhos de um question_group (uma consulta que executa um sindicato entre as duas tabelas: questionGroup :: getChildren ($ id)). Além disso, ao exibir o questionário com a resposta da pergunta, são necessárias duas consultas adicionais para recuperar o entrevistado e sua resposta a cada pergunta.

Enquanto o tempo de carregamento da página não é muito longo, essa abordagem parece errada. Recursão mais várias consultas de banco de dados para quase todos os nós não me fazem sentir muito quente e confusa por dentro.

eu tentei sem recursão e métodos recursivos na matriz completa de árvores retornou da união para construir uma matriz hierárquica para atravessar e exibir. No entanto, isso parece quebrar, pois existem IDs de nó duplicados devido ao fato de que grupos e perguntas são armazenados em tabelas separadas. Talvez eu esteja perdendo algo lá ...

Atualmente, a lógica para exibir a árvore nos formatos listados acima é uma bagunça. Prefiro não duplicar a lógica de travessia por todo o lado. No entanto, condicionais em todo o lugar também não produzem o código mais facilmente sustentável. Eu li sobre visitantes, decoradores e alguns dos iteradores do PHP, mas ainda estou me sentindo claro sobre como tudo isso funcionaria em conjunto com as classes que estão estendendo Zend_DB_TABLE, ZEND_DB_TABLE_ROWSET e ZEND_DB_TABLE_ROW. Especialmente porque não resolvi o problema anterior de construir a hierarquia a partir do banco de dados. Seria bom adicionar novos formatos de exibição (ou modificar os existentes) com um pouco facilmente.

Foi útil?

Solução

  • A lista de adjacência tradicionalmente lhe dá um parent_id coluna em cada linha que vincula uma linha ao seu pai imediato. o parent_id é nulo se a linha for a raiz de uma árvore. Mas isso leva você a executar muitas consultas SQL, o que é caro.

  • Adicione outra coluna root_id Portanto, cada linha sabe a que árvore ela pertence. Dessa forma, você pode buscar todos os nós de uma determinada árvore com uma única consulta SQL. Adicione um método ao seu Table classe para buscar um Rowset pelo id de raiz da árvore.

    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;
        }
    }
    
  • Escreva uma aula personalizada estendendo Zend_Db_Table_Row e escreva funções para recuperar os pais da linha dada e também um Rowset de seus filhos. o Row A classe deve conter objetos de dados protegidos para fazer referência aos pais e à matriz de filhos. UMA Row objeto também pode ter um getLevel() função e a getAncestorsRowset() função para farinha de rosca.

    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() {}
    }
    
  • Escreva uma aula personalizada estendendo Zend_Db_Table_Rowset Isso tem uma função para iterar sobre as linhas no lixo, definindo referências de pais e filhos para que você possa percorrer posteriormente -as como uma árvore. Também o Rowset deve ter um getRootRow() função.

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

Agora você pode ligar getRootRow() em um lixo e retorna o nó raiz. Depois de ter o nó raiz, você pode ligar getChildren() e percorrer sobre eles. Então você pode ligar getChildren() Também em qualquer uma dessas crianças intermediárias e produz uma árvore recursivamente em qualquer formato que desejar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top