Ver Mensaje Individual
  #5 (permalink)  
Antiguo 27/08/2013, 10:12
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\NodeList
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. use ArrayIterator;
  14. use InvalidArgumentException;
  15. use OutOfBoundsException;
  16.  
  17. /**
  18.  * Implementación de \Diba\Tree\NodeList.
  19.  */
  20. class NodeList implements INodeList
  21. {
  22.     /**
  23.      * El comparador para odernar los nodos.
  24.      *
  25.      * @var callable|int
  26.      */
  27.     protected $comparator;
  28.    
  29.     /**
  30.      * Colección de nodos.
  31.      *
  32.      * @var array
  33.      */
  34.     protected $nodes = [];
  35.    
  36.     // \ArrayAccess
  37.    
  38.     /**
  39.      * Indica si un índice existe.
  40.      *
  41.      * @see http://php.net/arrayaccess.offsetexists
  42.      *
  43.      * @param int $index El índice a ser comprobado.
  44.      * @return bool
  45.      */
  46.     public function offsetExists($index)
  47.     {
  48.         return (int) $index >= 0 && (int) $index < count($this->nodes);
  49.     }
  50.    
  51.     /**
  52.      * Devuelve el nodo contenido por el índice especificado.
  53.      *
  54.      * @see http://php.net/arrayaccess.offsetget
  55.      *
  56.      * @param int $index El índice del nodo a devolver.
  57.      * @return \Diba\Tree\Node
  58.      * @throws \OutOfBoundsException
  59.      * Si el índice está fuera de rango.
  60.      */
  61.     public function offsetGet($index)
  62.     {
  63.         $index = (int) $index;
  64.        
  65.         if ($index < 0 || $index >= count($this->nodes)) {
  66.             throw new OutOfBoundsException('Index is out of range');
  67.         }
  68.        
  69.         return $this->nodes[$index];
  70.     }
  71.    
  72.     /**
  73.      * Inserta un nodo en el índice especificado, y mueve todos los nodos
  74.      * siguientes hacia la derecha, incrementando su índice en: $index + 1.
  75.      *
  76.      * @see http://php.net/arrayaccess.offsetset
  77.      *
  78.      * @param int $index Índice donde se insertará el nodo.
  79.      * @param mixed $node El nodo a insertar.
  80.      * @return void
  81.      * @throws \InvalidArgumentException
  82.      * Si el nodo no implementa a \Diba\Tree\Node.
  83.      * @throws \OutOfBoundsException
  84.      * Si el índice está fuera de rango.
  85.      */
  86.     public function offsetSet($index, $node)
  87.     {
  88.         if (!$node instanceof INode) {
  89.             throw new InvalidArgumentException(
  90.                 'Invalid node type: instance of "\Diba\Tree\Node" expected, '
  91.                     .gettype($node). ' given'
  92.             );
  93.         }
  94.        
  95.         if ($index === null) {
  96.             $this->nodes[] = $node;
  97.            
  98.             return;
  99.         } elseif (!$this->offsetExists($index)) {
  100.             throw new OutOfBoundsException('Index is out of range');
  101.         }
  102.        
  103.         $index = (int) $index;
  104.        
  105.         if ($index === 0) {
  106.             array_unshift($this->nodes, $node);
  107.                
  108.             return;
  109.         }
  110.        
  111.         $right = array_splice($this->nodes, $index);
  112.         $this->nodes[] = $node;
  113.         $this->nodes = array_merge($this->nodes, $right);
  114.     }
  115.    
  116.     /**
  117.      * Quita el nodo contenido por el índice especificado, y mueve todos los
  118.      * nodos siguientes hacia la izquierda, reduciendo su índice en: $index - 1.
  119.      *
  120.      * @see http://php.net/arrayaccess.offsetunset
  121.      *
  122.      * @param int $index Índice del nodo a ser quitado.
  123.      * @return void
  124.      * @throws \OutOfBoundsException
  125.      * Si el índice está fuera de rango.
  126.      */
  127.     public function offsetUnset($index)
  128.     {
  129.         $index = (int) $index;
  130.        
  131.         if ($index < 0 || $index >= count($this->nodes)) {
  132.             throw new OutOfBoundsException('Index is out of range');
  133.         }
  134.        
  135.         array_splice($this->nodes, $index, 1);
  136.     }
  137.    
  138.     // \Countable
  139.    
  140.     /**
  141.      * Devuelve el número de nodos en ésta colección.
  142.      *
  143.      * @see http://php.net/class.countable
  144.      *
  145.      * @return int
  146.      */
  147.     public function count()
  148.     {
  149.         return count($this->nodes);
  150.     }
  151.    
  152.     // \IteratorAggregate
  153.    
  154.     /**
  155.      * Devuelve un iterador externo.
  156.      *
  157.      * @see http://php.net/class.iteratoraggregate
  158.      *
  159.      * @return \Iterator
  160.      */
  161.     public function getIterator()
  162.     {
  163.         return new ArrayIterator($this->nodes);
  164.     }
  165.    
  166.     // \Diba\Tree\NodeList
  167.    
  168.     /**
  169.      * Agrega un nodo a la colección.
  170.      *
  171.      * @param \Diba\Tree\Node $node El nodo que será agregado.
  172.      * @return void
  173.      */
  174.     public function add(INode $node)
  175.     {
  176.         $this->nodes[] = $node;
  177.     }
  178.    
  179.     /**
  180.      * Limpia la colección de nodos.
  181.      *
  182.      * @return void
  183.      */
  184.     public function clear()
  185.     {
  186.         $this->nodes = [];
  187.     }
  188.    
  189.     /**
  190.      * Indica si la colección contiene el nodo especificado.
  191.      *
  192.      * @param \Diba\Tree\Node $node El nodo a buscar.
  193.      * @return bool
  194.      */
  195.     public function contains(INode $node)
  196.     {
  197.         $search = array_search($node, $this->nodes, true);
  198.        
  199.         return ($search !== false);
  200.     }
  201.    
  202.     /**
  203.      * Indica si la colección de nodos está vacía.
  204.      *
  205.      * @return bool
  206.      */
  207.     public function isEmpty()
  208.     {
  209.         return empty($this->nodes);
  210.     }
  211.    
  212.     /**
  213.      * Quita el nodo especificado de la colección.
  214.      *
  215.      * @param \Diba\Tree\Node $node El nodo a quitar.
  216.      * @return void
  217.      */
  218.     public function remove(INode $node)
  219.     {
  220.         $previousCount = count($this->nodes);
  221.        
  222.         while (($index = $this->indexOf($node)) > -1) {
  223.             unset($this->nodes[$index]);
  224.         }
  225.        
  226.         if ($previousCount < count($this->nodes)) {
  227.             $this->nodes = array_values($this->nodes);
  228.         }
  229.     }
  230.    
  231.     /**
  232.      * Devuelve una copia de la colección, como array.
  233.      *
  234.      * @return array
  235.      */
  236.     public function toArray()
  237.     {
  238.         return $this->nodes;
  239.     }
  240.    
  241.     // \Diba\GenericTree\NodeList
  242.    
  243.     /**
  244.      * Inserta un nodo en el índice especificado, y mueve todos los nodos
  245.      * siguientes hacia la derecha, incrementando su índice en: $index + 1.
  246.      *
  247.      * @param int $index Índice del nodo a ser insertado.
  248.      * @param \Diba\Tree\Node $node El nodo a insertar.
  249.      * @return void
  250.      * @throws \OutOfBoundsException
  251.      * Si el índice está fuera de rango.
  252.      */
  253.     public function addAt($index, INode $node)
  254.     {
  255.         $this->offsetSet($index, $node);
  256.     }
  257.    
  258.     /**
  259.      * Devuelve el nodo contenido por el índice especificado.
  260.      *
  261.      * @param int $index El índice del nodo a obtener.
  262.      * @return \Diba\Tree\Node
  263.      * @throws \OutOfBoundsException
  264.      * Si el índice está fuera de rango.
  265.      */
  266.     public function get($index)
  267.     {
  268.         return $this->offsetGet($index);
  269.     }
  270.    
  271.     /**
  272.      * Devuelve el comparador de nodos.
  273.      *
  274.      * @return callable|int
  275.      */
  276.     public function getComparator()
  277.     {
  278.         return $this->comparator;
  279.     }
  280.    
  281.     /**
  282.      * Devuelve el primer índice de un nodo, si existe.
  283.      *
  284.      * @param \Diba\Tree\Node $node El nodo a buscar.
  285.      * @return int -1 si el índice no existe.
  286.      */
  287.     public function indexOf(INode $node)
  288.     {
  289.         $search = array_search($node, $this->nodes, true);
  290.        
  291.         return ($search !== false ? $search : -1);
  292.     }
  293.    
  294.     /**
  295.      * Une la colección de nodos especificada con ésta mísma.
  296.      *
  297.      * @param \Diba\Tree\NodeList $nodeList La colección de nodos a unir.
  298.      * @return bool Verdadero si ésta colección ha cambiado.
  299.      */
  300.     public function merge(INodeList $nodeList)
  301.     {
  302.         $previousCount = count($this->nodes);
  303.        
  304.         // Manipular hasta 3 nodos manualmente
  305.         switch ($nodeList->count()) {
  306.             case 0:
  307.                 return false;
  308.                 break;
  309.             case 1:
  310.                 $this->nodes[] = $nodeList->get(0);
  311.                 break;
  312.             case 2:
  313.                 $this->nodes[] = $nodeList->get(0);
  314.                 $this->nodes[] = $nodeList->get(1);
  315.                 break;
  316.             case 3:
  317.                 $this->nodes[] = $nodeList->get(0);
  318.                 $this->nodes[] = $nodeList->get(1);
  319.                 $this->nodes[] = $nodeList->get(2);
  320.                 break;
  321.             default:
  322.                 if ($previousCount < 1) {
  323.                     $this->nodes = $nodeList->toArray();
  324.                 } else {
  325.                     array_merge($this->nodes, $nodeList->toArray());
  326.                 }
  327.                 break;
  328.         }
  329.        
  330.         return count($this->nodes) > $previousCount;
  331.     }
  332.    
  333.     /**
  334.      * Quita el nodo contenido por el índice especificado, y mueve todos los
  335.      * nodos siguientes hacia la izquierda, reduciendo su índice en: $index - 1.
  336.      *
  337.      * @param int $index Índice del nodo a ser quitado.
  338.      * @return \Diba\Tree\Node El nodo previo en el índice especificado.
  339.      * @throws \OutOfBoundsException
  340.      * Si el índice está fuera de rango.
  341.      */
  342.     public function removeAt($index)
  343.     {
  344.         $previousNode = $this->offsetGet($index);
  345.         $this->offsetUnset($index);
  346.        
  347.         return $previousNode;
  348.     }
  349.    
  350.     /**
  351.      * Establece el nodo en el índice especificado, y sobreescribe al anterior.
  352.      *
  353.      * @param int $index El índice del nodo a establecer.
  354.      * @param \Diba\Tree\Node $node El nodo a establecer.
  355.      * @return \Diba\Tree\Node El nodo previo, si existía.
  356.      * @throws \OutOfBoundsException
  357.      * Si el índice está fuera de rango.
  358.      */
  359.     public function set($index, INode $node)
  360.     {
  361.         $previousNode = $this->offsetGet($index);
  362.         $this->nodes[$index] = $node;
  363.        
  364.         return $previousNode;
  365.     }
  366.    
  367.     /**
  368.      * Establece el nodo padre para todos los nodos de ésta colección.
  369.      *
  370.      * @param \Diba\Tree\Node $node El nodo padre.
  371.      * @return bool Verdadero si la ejecución ha cambiado a los nodos.
  372.      */
  373.     public function setParent(INode $node)
  374.     {
  375.         // Manipular hasta 3 nodos manualmente
  376.         switch (count($this->nodes)) {
  377.             case 0:
  378.                 return false;
  379.                 break;
  380.             case 1:
  381.                 $this->nodes[0]->setParent($node);
  382.                 break;
  383.             case 2:
  384.                 $this->nodes[0]->setParent($node);
  385.                 $this->nodes[1]->setParent($node);
  386.                 break;
  387.             case 3:
  388.                 $this->nodes[0]->setParent($node);
  389.                 $this->nodes[1]->setParent($node);
  390.                 $this->nodes[2]->setParent($node);
  391.                 break;
  392.             default:
  393.                 foreach ($this->nodes as $childNode) {
  394.                     $childNode->setParent($node);
  395.                 }
  396.                 break;
  397.         }
  398.        
  399.         return true;
  400.     }
  401.    
  402.     /**
  403.      * Establece el comparador, y devuelve el anterior.
  404.      *
  405.      * @param callable|int $comparator El callable o integer "flags".
  406.      * @return callable|int
  407.      * @throws \InvalidArgumentException
  408.      * Cuando el argumento $comparator no es de tipo callable o integer.
  409.      */
  410.     public function setComparator($comparator)
  411.     {
  412.         if (is_callable($comparator) || is_int($comparator)) {
  413.             $previousComparator = $this->comparator;
  414.             $this->comparator = $comparator;
  415.            
  416.             return $previousComparator;
  417.         } else {
  418.             throw new InvalidArgumentException(sprintf(
  419.                 'Invalid $comparator type: callable/int expected, %s given',
  420.                 gettype($comparator)
  421.             ));
  422.         }
  423.     }
  424.    
  425.     /**
  426.      * Ordena los nodos de acuerdo al comparador establecido.
  427.      *
  428.      * @return void
  429.      */
  430.     public function sort()
  431.     {
  432.         if (is_callable($this->comparator)) {
  433.             usort($this->nodes, $this->comparator);
  434.         } else {
  435.             sort($this->nodes, $this->comparator);
  436.         }
  437.     }
  438. }

(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! :-)