Domanda

I pensare il mio problema si riduce a due domande:

  1. Come faccio a costruire una struttura ad albero percorribile in PHP quando l'albero è memorizzato in MySQL (tra due tabelle) utilizzando l'approccio Adiacenze Elenco modelli, mantenendo le prestazioni in mente?

  2. Che cosa è un approccio mantenibile a visualizzare l'albero nei formati necessari senza duplicare il codice attraversamento e sporcare la logica con if / else e switch?

Di seguito sono riportati ulteriori dettagli:

Sto utilizzando Zend Framework.

Sto lavorando con un questionario. E 'conservato in un database MySQL tra i due distinti tavoli: domande e question_groups. Ciascuna tabella estende classi corrispondenti Zend_Db_Table_ *. La gerarchia è rappresentata utilizzando l'approccio Adiacenze Elenco modelli.

Mi rendo conto che i problemi che sto funzionando in è probabilmente dovuto al fatto che sto ripieno una struttura ad albero in un RDBMS, quindi sono aperto a soluzioni alternative. Tuttavia, sto anche la memorizzazione di intervistati al questionario e le loro risposte in modo da approcci alternativi avrebbero bisogno di sostenere questo.

Il questionario deve essere visualizzato in vari formati HTML:

  1. Come forma di inserimento risposte (utilizzando Zend_Form)
  2. Come un elenco ordinato (nidificato) con domande (e alcuni gruppi) come i collegamenti per visualizzare le risposte per domanda o per gruppo.
  3. Come un elenco ordinato (nidificato) con le risposte allegati ad ogni domanda.

Le domande sono nodi foglia e question_groups possono contenere altri question_groups e / o domande. Combinato, ci sono un po 'più di 100 righe da elaborare e visualizzare.

Attualmente, ho un aiutante vista che fa tutto il processo utilizzando la ricorsione per recuperare i figli di un question_group (una query che esegue un'unione tra le due tabelle: QuestionGroup :: getChildren ($ id)). Inoltre quando la visualizzazione questionario con la risposta domanda di ulteriori due query sono necessari per recuperare il convenuto e la loro risposta ad ogni domanda.

Mentre il tempo di caricamento della pagina non è molto lunga questo approccio si sente male. Ricorsione più più query di database per quasi ogni nodo non mi fanno sentire molto caldo e al sicuro all'interno.

Ho provato ricorsione -less e metodi ricorsivi sulla gamma completa dell'albero restituiti dall'unione di costruire una matrice gerarchica per attraversare e visualizzazione. Tuttavia, sembra che per abbattere in quanto non vi sono duplicati ID dei nodi a causa del fatto che i gruppi e le domande sono memorizzati in tabelle separate. Forse mi manca qualcosa là ...

Al momento, la logica per visualizzare l'albero nei formati elencati sopra è un bel pasticcio. Preferirei non duplicare la logica di attraversamento in tutto il luogo. Tuttavia, i condizionali in tutto il posto non producono il codice più facilmente mantenibile sia. Ho letto su Visitatori, decoratori e alcuni dei iteratori PHP SPL ma mi sento ancora chiaro in che modo sarebbe lavorare tutti insieme con le classi che vengono estendono Zend_Db_Table, Zend_Db_Table_Rowset e Zend_Db_Table_Row. Soprattutto visto che non ho risolto il problema precedente della costruzione della gerarchia dal database. Sarebbe bello per aggiungere nuovi formati di visualizzazione (o modificare quelli esistenti) un po 'facile.

È stato utile?

Soluzione

  • La lista di adiacenza tradizionalmente ti dà una colonna parent_id in ogni riga che si collega una riga al suo genitore immediato. Il parent_id è NULL se la riga è la radice di un albero. Ma questo porta ad eseguire molte query SQL, che è costoso.

  • Aggiungi un altro root_id colonna in modo ogni riga sa quale albero a cui appartiene. In questo modo è possibile recuperare tutti i nodi di un determinato albero con una singola query SQL. Aggiungere un metodo alla classe Table per andare a prendere un Rowset da root id dell'albero.

    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;
        }
    }
    
  • Scrivi una estensione Zend_Db_Table_Row classe personalizzata e funzioni di scrittura per recuperare la data genitore riga e anche una Rowset dei suoi figli. La classe Row dovrebbe contenere oggetti di dati protetti per fare riferimento al genitore e la matrice dei bambini. Un oggetto Row può anche avere una funzione getLevel() e una funzione per getAncestorsRowset() pangrattato.

    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() {}
    }
    
  • Scrivi una Zend_Db_Table_Rowset estende classe personalizzata che ha una funzione per scorrere le righe nel set di righe, l'impostazione genitori e bambini riferimenti in modo da poter successivamente li attraversare come un albero. Anche il Rowset dovrebbe avere una funzione di 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;
              }
            }
        }
    }
    

Ora è possibile chiamare getRootRow() su un set di righe, e restituisce il nodo principale. Una volta che hai il nodo principale, è possibile chiamare getChildren() e anello su di loro. Poi si può chiamare getChildren() anche su uno di questi bambini intermedi, e ricorsivamente un albero di uscita in qualsiasi formato che si desidera.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top