Foros del Web » Programando para Internet » PHP » Frameworks y PHP orientado a objetos »

PHP OO Problema con definicion de una clase

Estas en el tema de Problema con definicion de una clase en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Holas!! tengo un problema con un metodo para clase que he creado, esta clase se llama Nodo y consiste basicamente en que puede anidar o ...
  #1 (permalink)  
Antiguo 23/04/2011, 14:06
Avatar de Danielfuzz  
Fecha de Ingreso: septiembre-2007
Mensajes: 111
Antigüedad: 16 años, 6 meses
Puntos: 18
Pregunta Problema con definicion de una clase

Holas!!

tengo un problema con un metodo para clase que he creado, esta clase se llama Nodo y consiste basicamente en que puede anidar o contener varios otros Nodos en su interior y asi sucesivamente (como árbol), para ello mi clase nodo tiene un miembro que es un arreglo en el que almacenará a todos su inmediatos nodos hijos.

Además mi clase tiene respectivamente metodos para anadir nodos y removerlos. Mas o menos mi clase quedaría así:

Código:
class Nodo
{
	public $nombre;
	public $valor;
	public $atributos=array();
	public $elementos=array();
	private $contador=0;
	public $padre=null;
	
	function __construct($nombre, $valor="")
	{
		$this->nombre=$nombre;
		$this->valor=$valor;
		$this->padre=$padre;
	}
	
	public function addAtributo($nombre,$valor)
	{
		$this->atributos[$nombre]=$valor;
	}
	
	public function removeAtributo($nombre)
	{
		unset($this->atributos[$nombre]);
	}
	
	public function addElemento($nodo)
	{
		$nodo->padre=$this;
		$this->elementos[$this->contador]=$nodo;
		$this->contador++;
	}
	
	public function removeElemento($nodo)
	{
		$llave=array_search($nodo, $this->elementos);
		
		$this->elementos[$llave]->padre=null;
		
		while(isset($this->elementos[$llave+1]))
		{
			$this->elementos[$llave]=$this->elementos[$llave+1];
			$llave++;
		}
		unset($this->elementos[$llave]);
		$this->contador--;
	}
}
En fin, aca el método conflictivo es el de removeElemento al que se le pasa como parámetro otro objeto de la misma clase (Nodo).
Dentro del método removeElemento hago uso de la función array_search que me devuelve la llave o índice de posición del elemento que le pasas como parámetro. Entonces teniendo ya el indice/llave puedo recolocar los otros elementos existentes sobreescribiendo la posicion del elemento que retiré para finalmente hacer unset a la ultima posicion del arrelgo (ya que el ultimo paso a ser penultimo).

Pero cuando pruebo mi clase mediante el siguiente script me da este error:

Cita:
Fatal error: Nesting level too deep - recursive dependency? in C:\AppServ\www\Nodo.php on line 38
La línea 38 es en la que invoco a la funcion array_search


el script en el que pruebo mi clase:

Código:
	include_once("Nodo.php");
	
	$nodo = new Nodo("raiz");
	$nodo->addAtributo("paginas",1);
	$nodo->addElemento(new Nodo("hijo")); \\si comento esto y las siguientes 3 líneas no se lanza ningún error
	$nodo->elementos[0]->addAtributo("id",1);
	$nodo->elementos[0]->addElemento(new Nodo("nombre"));
	$nodo->elementos[0]->elementos[0]->valor="Daniel";
	
	echo "<br>nodo raíz tiene ".count($nodo->elementos)." elementos";
	
	$aux=$nodo->elementos[0];
	
	$nodo->removeElemento($aux);
	
	echo "<br>nodo raíz tiene ".count($nodo->elementos)." elementos";
Si les sirve de algo el error no sucede cuando el nodo a retirar no posee nodos hijos.

ahora no sé si la mejor solución sería cambiar la manera en la que hago la removida de mi nodo hijo.

Saludos.
  #2 (permalink)  
Antiguo 23/04/2011, 14:34
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 3 meses
Puntos: 845
Respuesta: Problema con definicion de una clase

Que tal Danielfuzz,

Te dejo un link http://www.richardlord.net/blog/php-...ive-dependency, a ver si con eso lo solucionas, te sugiero que las propiedades no sean públicas no es una buena practica, el constructor no tiene declarada la visibilidad, ademas asignas la variable $padre que no se pasa por parámetro, y deberías implementar las interfaces Iterator y Countable.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #3 (permalink)  
Antiguo 23/04/2011, 19:30
Avatar de Danielfuzz  
Fecha de Ingreso: septiembre-2007
Mensajes: 111
Antigüedad: 16 años, 6 meses
Puntos: 18
De acuerdo Respuesta: Problema con definicion de una clase

Hola masterpuppet, gracias por la respuesta, la verda me sirvió, con sólo leer las primeras líneas ya supe que es lo que tenía que cambiar.

La solución era agregarle un parametro mas a la funcion array_search, que es un booleano que indice si la busqueda será estricta o no, quedando esa línea así:

Código:
$llave=array_search($nodo, $this->elementos, true);
La verdad soy nuevo en esto de la programación POO en PHP, así que no se si me pordrias indicar porqué implementar las interfaces Iterator y Countable???.

Saludos, y Gracias, ahí te va algo de karma
  #4 (permalink)  
Antiguo 24/04/2011, 05:39
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 3 meses
Puntos: 845
Respuesta: Problema con definicion de una clase

La idea es implementar Iterator para(valga la redundancia) iterar directamente sobre los childrens de la clase Node, algo así:

Código PHP:
Ver original
  1. $node = new Node('Root');
  2. $node->addChild(new Node('Child 1'));
  3. $node->addChild(new Node('Child 2'));
  4. $node->addChild(new Node('Child 3'));
  5.  
  6. foreach($node as child){
  7.     print_r($child);//child 1, child 2, child 3
  8. }

y Countable para saber la cantidad de childrens aplicando count directamente al objeto:

Código PHP:
Ver original
  1. $node = new Node('Root');
  2. $node->addChild(new Node('Child 1'));
  3. $node->addChild(new Node('Child 2'));
  4. $node->addChild(new Node('Child 3'));
  5.  
  6. echo count($node);// 3

Te sugiero que leas sobre SPL

Agregado:

Otra opción es implementar RecursiveIterator para en vez de recorrer solamente los hijos directos de un nodo, puedas recorrer toda la descendencia.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)

Última edición por masterpuppet; 24/04/2011 a las 05:50 Razón: RecursiveIterator
  #5 (permalink)  
Antiguo 24/04/2011, 10:39
Avatar de Danielfuzz  
Fecha de Ingreso: septiembre-2007
Mensajes: 111
Antigüedad: 16 años, 6 meses
Puntos: 18
De acuerdo Respuesta: Problema con definicion de una clase

Gracias masterpuppet realmente estas son el tipo de respuestas que necesito y me refiero a "talvez esto deberias hacerlo así", "estará mejor si lo haces así", por q creo q la buenoas respuestas no son solo aquellas en las que te dicen como resolver tu problema sino aquellas que te sugieren mejores maneras de hacerlo.

Muchas Gracias, estaré leyendo lo que me indicas, y aunq sé lo q teóricamente es una interface nunca lo utilicé y creo q no hay tiempo que perder y ponerse a leer y aprender.

Con respecto a RecursiveIterator yo ya tengo un metodo recursivo que te recorre todos los nodos en un orden particular (Primero padre y depues hijos), pero este metodo se encuentra en otra clase que tambien creé (la clase XmlDocument). Lo que pasa es que estoy creando mi propia clase para generar archivos XML, y por lo tanto basicamente esto abarca dos clases:
  1. la clase XmlDocument que es la que representará al documento en sí
  2. la clase Nodo, que será cada uno de los nodos/etiquetas del xml

Precisamente la clase XmlDocument es la que contiente el metodo para recorrer todos los nodos hijos a partir de su nodo raíz, pero como dices sería mejor acceder a ellos directamente por medio del objeto.

bueno, me pongo a leer cómo implementar una interfacce pues teóricamente lo único que sé es que para implementar se debe agregar "implements nombreInterface" inmediatamente despues del nombre de la clase y esciribr los metodos de dicha interface dentro de ella, pero supongo que tengo que hallar una forma de conectar mi array de nodos hijos con el metodo de la interface para que haga el conteo en dicho array.

muchas gracias por la ayuda, espero poder seguir recibiendo respuestas como las tuyas.

SAludos!!
  #6 (permalink)  
Antiguo 24/04/2011, 13:38
Avatar de Danielfuzz  
Fecha de Ingreso: septiembre-2007
Mensajes: 111
Antigüedad: 16 años, 6 meses
Puntos: 18
Pregunta Respuesta: Problema con definicion de una clase

hola de nuevo masterpuppet, primeramente mil gracias por recomendarme las interfaces pues te comento que ya implementé la interface Countable en mi clase y funciona perfectamente. También estoy tratando de implementar la interface RecursiveIterator, ya he implementado todos su métodos a excepción de uno: el getChildren() que viendo la ayuda de PHP te debe devolver un objeto de la clase RecursiveIterator y no existe ningún ejemplo de cómo se podria hacer uso de esta interface, por favor no sé si podrias darme una mano con este método más.

Definición de la interface RecursiveIterator

Código PHP:
RecursiveIterator extends Iterator {
/* Métodos */
public RecursiveIterator getChildren void )
public 
bool hasChildren void )
/* Métodos heredados */
abstract public mixed Iterator::current void )
abstract public 
scalar Iterator::key void )
abstract public 
void Iterator::next void )
resumenpúblicovoid Iterator::rewind void )
resumen público boolean Iterator::valid void )

Mi clase:
Código PHP:
<?

class Nodo implements CountableRecursiveIterator
{
    public 
$nombre;
    public 
$valor;
    public 
$atributos=array();
    private 
$elementos=array();
    private 
$contador=0;
    private 
$actual=0;
    public 
$padre=null;
    
    function 
__construct($nombre$valor="")
    {
        
$this->nombre=$nombre;
        
$this->valor=$valor;
        
$this->padre=$padre;
    }
    
    public function 
addAtributo($nombre,$valor)
    {
        
$this->atributos[$nombre]=$valor;
    }
    
    public function 
removeAtributo($nombre)
    {
        unset(
$this->atributos[$nombre]);
    }
    
    public function 
addElemento($nodo)
    {
        
$nodo->padre=$this;
        
$this->elementos[$this->contador]=$nodo;
        
$this->contador++;
    }
    
    public function 
elemento($i)
    {
        return 
$this->elementos[$i];
    }
    
    public function 
removeElemento($nodo)
    {
        
$llave=array_search($nodo$this->elementos,true);
        
        echo 
"llave".$llave;
        
        
$this->elementos[$llave]->padre=null;
        
        while(isset(
$this->elementos[$llave+1]))
        {
            
$this->elementos[$llave]=$this->elementos[$llave+1];
            
$llave++;
        }
        unset(
$this->elementos[$llave]);
        
$this->contador--;
    }
    
    
//MEtodos de interfaz
    
    
public function count(){
        return 
count($this->elementos);
    }
    
    public function 
getChildren()
    {
    }
    
    public function 
hasChildren()
    {
        if(
count($this->elementos)>0)
            return 
true;
        else
            return 
false;
    }
    
    public function 
rewind()
    {
        
$this->actual=0;    
    }
    
    public function 
current()
    {
        return 
$this->elementos[$this->actual];
    }
    
    public function 
key(){
        return 
$this->actual;
    }
    
    public function 
next(){
        ++
$this->actual;
    }
    
    public function 
valid(){
        return isset(
$this->elementos[$this->actual]);
    }
}

?>
Mi script de prueba:
Código PHP:
<?
include_once("Nodo.php");
    
    
$nodo = new Nodo("raiz");
    
$nodo->addAtributo("paginas",1);
    
$nodo->addElemento(new Nodo("elemento"));
    
$nodo->elemento(0)->addAtributo("id",1);
    
$nodo->elemento(0)->addElemento(new Nodo("nombre"));
    
$nodo->elemento(0)->elemento(0)->valor="Daniel";
    
    echo 
"nodo raíz tiene ".count($nodo)." elementos";
    
    
$aux=$nodo->elemento(0);
    
    
$nodo->removeElemento($aux);
    
    echo 
"nodo raíz tiene ".count($nodo)." elementos";
?>
Saludos
  #7 (permalink)  
Antiguo 24/04/2011, 15:07
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 3 meses
Puntos: 845
Respuesta: Problema con definicion de una clase

La documentación dice que tiene que devolver un RecursiveIterator, en tu caso los nodos implementan dicha interface, así que lo que único que tienes que hacer es devolver el elemento actual.

Código PHP:
Ver original
  1. public function getChildren()
  2. {
  3.     return $this->current();
  4. }

Aunque yo no utilizaría un contador, podría ser así(deje solo esencial, para que quede mas claro el ejemplo):


Código PHP:
Ver original
  1. class Node implements RecursiveIterator, Countable
  2. {
  3.     /**
  4.      * @var string
  5.      */
  6.     private $name;
  7.  
  8.     /**
  9.      * @var array
  10.      */
  11.     private $childrens = array();
  12.  
  13.     /**
  14.      * @var Node
  15.      */
  16.     private $parent;
  17.  
  18.     /**
  19.      * @param string $name
  20.      * @param Node|null $parent
  21.      */
  22.     public function __construct($name, $parent = null)
  23.     {
  24.         $this->name   = $name;
  25.         $this->parent = $parent;
  26.     }
  27.  
  28.     /**
  29.      * @param Node $n
  30.      * @return Node provide fluent interface
  31.      */
  32.     public function addChild(Node $n)
  33.     {
  34.         $n->setParent($this);
  35.         $this->childrens[] = $n;
  36.         return $this;
  37.     }
  38.  
  39.     /**
  40.      * @return string $name
  41.      */
  42.     public function getName()
  43.     {
  44.         return $this->name;
  45.     }
  46.  
  47.     /**
  48.      * @param Node $n
  49.      * @return Node provide fluent interface
  50.      */
  51.     public function setParent(Node $n)
  52.     {
  53.         $this->parent = $n;
  54.         return $this;
  55.     }
  56.  
  57.     /**
  58.      * Countable Interface
  59.      */
  60.     public function count()
  61.     {
  62.         return count($this->childrens);
  63.     }
  64.  
  65.     /**
  66.      * RecrusiveIterator Interface
  67.      */
  68.     public function getChildren()
  69.     {
  70.         return $this->current();
  71.     }
  72.  
  73.     public function hasChildren()
  74.     {
  75.         return count($this->childrens) > 0 ? true : false;
  76.     }
  77.  
  78.  
  79.     public function rewind()
  80.     {
  81.         reset($this->childrens);
  82.     }
  83.  
  84.     public function current()
  85.     {
  86.         return current($this->childrens);
  87.     }
  88.  
  89.     public function key(){
  90.         return key($this->childrens);
  91.     }
  92.  
  93.     public function next()
  94.     {
  95.         next($this->childrens);
  96.     }
  97.  
  98.     public function valid()
  99.     {
  100.         return false !== $this->current();
  101.     }
  102.  
  103. }

Debes recordar que para iterar sobre todos los nodos hay que envolver el RecursiveIterator en un RecursiveIteratorIterator, un ejemplo:

Código PHP:
Ver original
  1. $node = new Node('Root');
  2.  
  3. $child1  = new Node('Node 1');
  4. $child1->addChild(new Node('Node 1.1'));
  5. $child1->addChild(new Node('Node 1.2'));
  6. $child1->addChild(new Node('Node 1.3'));
  7. $node->addChild($child1);
  8. $node->addChild(new Node('Node 2'));
  9. $node->addChild(new Node('Node 3'));
  10.  
  11. foreach(new RecursiveIteratorIterator($node,
  12.                 RecursiveIteratorIterator::SELF_FIRST) as $child) {
  13.     echo $child->getName() . ' :: ' . count($child) . PHP_EOL;
  14. }

En cuanto a que trabajas con XML, hay un iterator para ese caso tambien SimpleXMLIterator, el cual implementa RecursiveIterator y Countable.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)

Última edición por masterpuppet; 24/04/2011 a las 16:24 Razón: typo
  #8 (permalink)  
Antiguo 24/04/2011, 20:34
Avatar de Danielfuzz  
Fecha de Ingreso: septiembre-2007
Mensajes: 111
Antigüedad: 16 años, 6 meses
Puntos: 18
Respuesta: Problema con definicion de una clase

Perfecto, muchas gracias masterpuppet, jaja empecé con un simple problema y termine aplicando interfaces y mejorando la definición de mi clase. Gracias!!!

Termine aprendiendo más de lo que pretendía al iniciar este tema, gracias de nuevo, ójala siempre sea así.

Saludos.

Etiquetas: arreglos, clases, iterator, oop, php, poo, recursiveiterator
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 1 personas




La zona horaria es GMT -6. Ahora son las 09:24.