Question

I penser ma question se résume à deux questions:

  1. Comment puis-je construire une structure arborescente traversable en PHP lorsque l'arbre est stocké dans MySQL (entre deux tables) en utilisant l'approche du modèle Liste contiguïté tout en gardant à l'esprit la performance?

  2. Qu'est-ce qu'une approche maintenable pour afficher l'arbre dans les formats nécessaires sans dupliquer le code traversal et de joncher la logique avec if / else et les instructions switch?

Voici plus de détails:

J'utilise le Zend Framework.

Je travaille avec un questionnaire. Il est stocké dans une base de données MySQL entre deux tables distinctes: questions et question_groups. Chaque table étend les classes appropriées Zend_Db_Table_ de *. La hiérarchie est représentée à l'aide de l'approche modèle Liste contiguïté.

Je me rends compte des problèmes que je suis en cours d'exécution en sont probablement en raison du fait que je suis bourrer une structure arborescente dans un SGBDR, donc je suis ouvert à des solutions de rechange. Cependant, je suis également stocker les répondants au questionnaire et leurs réponses si des approches alternatives devraient soutenir cela.

Le questionnaire doit être affiché dans différents formats HTML:

  1. En tant que forme pour entrer les réponses (en utilisant Zend_Form)
  2. Comme une liste ordonnée (imbriquée) avec des questions (et certains groupes ) sous forme de liens pour afficher les réponses par question ou par groupe.
  3. Comme une liste ordonnée (imbriquée) avec des réponses jointes à chaque question.

Les questions sont les nœuds feuilles et question_groups peuvent contenir d'autres question_groups et / ou des questions. Ensemble, il y a un peu plus de 100 lignes à traiter et d'afficher.

À l'heure actuelle, j'ai une aide de vue qui fait tout le traitement à l'aide récursion pour récupérer les enfants d'un question_group (une requête qui effectue une union entre les deux tables: QuestionGroup :: getChildren ($ id)). De plus, lorsque questionnaire affichant la réponse de la question sont nécessaires un deux requêtes supplémentaires pour récupérer le répondant et leur réponse à chaque question.

Alors que le temps de chargement de la page est pas très longtemps cette approche se sent mal. Récursion, plus plusieurs requêtes de base de données pour presque chaque nœud ne fait pas me sentir très chaud et floue à l'intérieur.

J'ai essayé récursion -moins et méthodes récursives sur la gamme complète de l'arbre retournés de l'Union pour construire un réseau hiérarchique de traverser et d'affichage. Cependant, cela semble se décomposer car il y a nœuds dupliqués ids en raison du fait que les groupes et les questions sont stockées dans des tables séparées. Peut-être que je manque quelque chose ...

À l'heure actuelle, la logique pour afficher l'arborescence dans les formats énumérés ci-dessus est tout un gâchis. Je préfère ne pas reproduire la logique traversal dans tous les sens. Cependant, conditionals dans tous les sens ne produisent pas non plus le code le plus facilement maintenable. J'ai lu sur Visiteurs, décorateurs et certains des itérateurs SPL PHP, mais je me sens toujours pas clair quant à comment cela pourrait fonctionner tous ensemble avec les classes qui étendent Zend_Db_Table, Zend_Db_Table_Rowset et Zend_Db_Table_Row. D'autant plus que je ne l'ai pas résolu le problème précédent de la construction de la hiérarchie de la base de données. Ce serait bien d'ajouter de nouveaux formats d'affichage (ou modifier ceux qui existent déjà) un peu facilement.

Était-ce utile?

La solution

  • La liste contiguïté vous donne traditionnellement une colonne de parent_id dans chaque ligne qui relie une ligne à son parent immédiat. Le parent_id est NULL si la ligne est la racine d'un arbre. Mais cela vous conduit à exécuter plusieurs requêtes SQL, ce qui est coûteux.

  • Ajoutez un autre root_id de colonne pour chaque ligne sait quel arbre il appartient. De cette façon, vous pouvez chercher tous les nœuds d'un arbre donné avec une seule requête SQL. Ajouter une méthode à votre classe de Table chercher un Rowset par id racine de l'arbre.

    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;
        }
    }
    
  • Ecrire une classe personnalisée extension Zend_Db_Table_Row et écrire des fonctions pour récupérer le parent de ligne donnée et aussi un Rowset de ses enfants. La classe Row doit contenir des objets de données protégées pour référencer le parent et l'ensemble des enfants. Un objet Row peut aussi avoir une fonction getLevel() et une fonction getAncestorsRowset() pour panure.

    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() {}
    }
    
  • Ecrire une classe personnalisée extension Zend_Db_Table_Rowset qui a une fonction à itérer sur les lignes de l'ensemble de lignes, la mise en références des parents et des enfants afin que vous puissiez ensuite les traverser comme un arbre. De plus, le Rowset doit avoir une fonction 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;
              }
            }
        }
    }
    

Maintenant, vous pouvez appeler getRootRow() sur un ensemble de lignes, et il retourne le nœud racine. Une fois que vous avez le nœud racine, vous pouvez appeler getChildren() et boucle sur eux. Ensuite, vous pouvez appeler getChildren() aussi sur l'un de ces enfants intermédiaires, et la sortie récursive un arbre dans un format que vous voulez.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top