Ver Mensaje Individual
  #7 (permalink)  
Antiguo 27/08/2013, 10:15
Nisrokh
 
Fecha de Ingreso: septiembre-2009
Ubicación: Neuquén
Mensajes: 142
Antigüedad: 15 años, 1 mes
Puntos: 12
Respuesta: [APORTE] Estructura de árbol desde de base de datos

Diba\GenericTree\TableTree
Código PHP:
Ver original
  1. <?php
  2.  
  3. /**
  4.  * @author Diego P. M. Baltar <[email protected]>
  5.  * @copyright (c) 2013
  6.  * @package Diba
  7.  * @subpackage GenericTree
  8.  */
  9. namespace Diba\GenericTree;
  10.  
  11. use Diba\Tree\Node as INode;
  12. use Diba\Tree\NodeList as INodeList;
  13.  
  14. /**
  15.  * Implementación genérica de un árbol, construída desde una tabla.
  16.  */
  17. class TableTree extends Node
  18. {
  19.     /**
  20.      * Mapa de nodos por su id.
  21.      *
  22.      * @var array
  23.      */
  24.     protected $nodes = [];
  25.    
  26.     /**
  27.      * Opciones de entrada y salida.
  28.      *
  29.      * @var \Diba\GenericTree\TableTreeOptions
  30.      */
  31.     protected $options;
  32.    
  33.     /**
  34.      * Mapa de colección de nodos "huérfanos" por su id de padre, es decir,
  35.      * aquellos que deberían tener un padre pero no fue encontrado.
  36.      *
  37.      * @var array
  38.      */
  39.     protected $orphans = [];
  40.    
  41.     /**
  42.      * Constructor
  43.      *
  44.      * @param \Diba\GenericTree\TableTreeOptions $options
  45.      * Opciones de entrada y salida.
  46.      * @return \Diba\GenericTree\TableTree
  47.      */
  48.     public function __construct(TableTreeOptions $options = null)
  49.     {
  50.         if ($options === null) {
  51.             $this->options = new TableTreeOptions();
  52.         } else {
  53.             $this->options = $options;
  54.         }
  55.     }
  56.    
  57.     /**
  58.      * Construye el árbol desde una tabla.
  59.      *
  60.      * @todo Detectar errores recursivos.
  61.      * @todo Detectar multiples ubicaciones (en un mísmo grupo).
  62.      *
  63.      * @param array $table La tabla de datos.
  64.      * @param bool $sort Indica si los nodos deben ser ordenados.
  65.      * @return bool Verdadero si el árbol ha cambiado.
  66.      */
  67.     public function build(array $table, $sort = false)
  68.     {
  69.         if (empty($table)) {
  70.             return false;
  71.         }
  72.        
  73.         // Acceso corto a los nombres de las claves de los ids de entrada
  74.         $idKey = $this->options->getIdKey();
  75.         $parentIdKey = $this->options->getParentIdKey();
  76.        
  77.         // Datos temporales
  78.         $onHold = [];            // The "on hold" list
  79.         $onHoldChildren = [];    // Children on hold nodes
  80.         $onHoldChildrenMap = []; // Children on hold map
  81.         $sortableChildren = [];  // Children that should be sorted
  82.        
  83.         // Agregar el árbol en sí, al mapa de nodos bajo el id "0"
  84.         $this->nodes[0] = $this;
  85.        
  86.         // Construir árbol
  87.         foreach ($table as $index => $row) {
  88.             // Forzar fila hacia un array, para obtener los ids
  89.             $row = (array) $row;
  90.             $nodeId = $row[$idKey];
  91.             $nodeParentId = $row[$parentIdKey];
  92.            
  93.             // Ignorar filas con clave "0"
  94.             if ((int) $nodeId === 0) {
  95.                 continue;
  96.             }
  97.            
  98.             // Crear nodo
  99.             $node = new Node($table[$index]);
  100.            
  101.             // Indica si el nodo padre del nodo actual está en espera
  102.             $parentIsOnHold = false;
  103.            
  104.             // ¿Se ha encontrado ya el nodo padre de este nodo?
  105.             if (isset($this->nodes[$nodeParentId])) {
  106.                 $parent = $this->nodes[$nodeParentId];
  107.             } elseif (isset($onHold[$nodeParentId])) {
  108.                 $onHoldKey = $onHold[$nodeParentId];
  109.                 $parent = $onHoldChildrenMap[$onHoldKey][$nodeParentId];
  110.                 $parentIsOnHold = true;
  111.             } elseif (isset($parent)) {
  112.                 unset($parent);
  113.             }
  114.            
  115.             // Dar nodo a su padre (si éste ha sido encontrado)
  116.             if (isset($parent)) {
  117.                 $parentChildren = $parent->getChildren();
  118.                 // Establecer el nodo padre y agregar sus hijos
  119.                 $node->setParent($parent);
  120.                 $parentChildren->add($node);
  121.                
  122.                 if ($parentIsOnHold) {
  123.                     // Agregar nodo al mapa de lista de espera
  124.                     $onHold[$nodeId] = $onHold[$nodeParentId];
  125.                     $onHoldChildrenMap[$onHold[$nodeId]][$nodeId] = $node;
  126.                 } else {
  127.                     // Agregar nodo al mapa final
  128.                     $this->nodes[$nodeId] = $node;
  129.                 }
  130.                
  131.                 // Agregar nodos hijos para ordenar, sólo si es necesario
  132.                 if ($sort && $parentChildren->count() > 1
  133.                         && !isset($sortableChildren[$nodeParentId])) {
  134.                     $sortableChildren[$nodeParentId] = $parentChildren;
  135.                 }
  136.             } else {
  137.                 // Agregar nodo a la lista de espera
  138.                 $onHold[$nodeId] = $nodeParentId;
  139.                
  140.                 if (!isset($onHoldChildren[$nodeParentId])) {
  141.                     $onHoldChildren[$nodeParentId] = new NodeList();
  142.                 }
  143.                
  144.                 if (!isset($onHoldChildrenMap[$nodeParentId])) {
  145.                     $onHoldChildrenMap[$nodeParentId] = [];
  146.                 }
  147.                
  148.                 // Agregar el nodo al mapa de lista de espera
  149.                 $onHoldChildren[$nodeParentId]->add($node);
  150.                 $onHoldChildrenMap[$nodeParentId][$nodeId] = $node;
  151.             }
  152.            
  153.             // ¿És este nodo el padre de alguno de los nodos en espera?
  154.             if (isset($onHoldChildren[$nodeId])) {
  155.                 $nodeChildren = $node->getChildren();
  156.                
  157.                 // Establecer el nodo padre, y agregar a sus hijos
  158.                 $onHoldChildren[$nodeId]->setParent($node);
  159.                 $nodeChildren->merge($onHoldChildren[$nodeId]);
  160.                
  161.                 // Agregar nodos al mapa final
  162.                 $this->nodes+= $onHoldChildrenMap[$nodeId];
  163.                
  164.                 // Borrar referencias del nodo hacia la lista de espera
  165.                 unset($onHoldChildren[$nodeId]);
  166.                 unset($onHoldChildrenMap[$nodeId]);
  167.                
  168.                 // Agregar nodos hijos para ordenar, sólo si es necesario
  169.                 if ($sort && $nodeChildren->count() > 1
  170.                         && !isset($sortableChildren[$nodeId])) {
  171.                     $sortableChildren[$nodeId] = $nodeChildren;
  172.                 }
  173.             }
  174.         }
  175.        
  176.         // Obtener los nodos "huérfanos", donde su parent_id != 0 pero de todas
  177.         // maneras no ha sido encontrado
  178.         if (!empty($onHoldChildren)) {
  179.             $this->orphans = $onHoldChildren;
  180.         }
  181.        
  182.         // Ordenar los nodos, sólo si es necesario
  183.         if (!empty($sortableChildren)) {
  184.             foreach ($sortableChildren as $unsortedChildren) {
  185.                 $unsortedChildren->setComparator(
  186.                     $this->options->getComparator()
  187.                 );
  188.                 $unsortedChildren->sort();
  189.             }
  190.         }
  191.        
  192.         return true;
  193.     }
  194.    
  195.     /**
  196.      * Devuelve un nodo obtenido por su id.
  197.      *
  198.      * @param scalar $id El id del nodo a buscar.
  199.      * @return \Diba\Tree\Node|null
  200.      */
  201.     public function getNodeById($id)
  202.     {
  203.         $id = (string) $id;
  204.         $node = null;
  205.        
  206.         if (isset($this->nodes[$id])) {
  207.             $node = $this->nodes[$id];
  208.         }
  209.        
  210.         return $node;
  211.     }
  212.    
  213.     /**
  214.      * Devuelve la instancia de las opciones de entrada y salida.
  215.      *
  216.      * @return \Diba\GenericTree\TableTreeOptions
  217.      */
  218.     public function getOptions()
  219.     {
  220.         return $this->options;
  221.     }
  222.    
  223.     /**
  224.      * Devuelve una colección de nodos "huérfanos", agrupados por el id de su
  225.      * padre (no encontrado).
  226.      *
  227.      * @return array
  228.      */
  229.     public function getOrphans()
  230.     {
  231.         return $this->orphans;
  232.     }
  233.    
  234.     /**
  235.      * Reestablece el árbol.
  236.      *
  237.      * @return void
  238.      * @override \Diba\GenericTree\Node::reset()
  239.      */
  240.     public function reset()
  241.     {
  242.         $this->nodes = [];
  243.         $this->orphans = [];
  244.        
  245.         parent::reset();
  246.     }
  247.    
  248.     /**
  249.      * Establece las opciones de entrada y salida, y devuelve las anteriores.
  250.      *
  251.      * @param \Diba\GenericTree\TableTreeOptions $options
  252.      * La nueva instancia de las opciones de entrada y salida.
  253.      * @return \Diba\GenericTree\TableTreeOptions
  254.      */
  255.     public function setOptions(TableTreeOptions $options)
  256.     {
  257.         $previousOptions = $this->options;
  258.         $this->options = $options;
  259.        
  260.         return $previousOptions;
  261.     }
  262. }

(Continúa en comentarios)
__________________
Amigos de Foros del Web: seamos más solidarios. ¡No dejemos que un tema se valla al final de las páginas con 0 (cero) respuestas! ¡Gracias por su ayuda! :-)