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

Duda de cómo hacer clases dependientes de otras.

Estas en el tema de Duda de cómo hacer clases dependientes de otras. en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Hola a todos, tengo la siguiente duda que no se de qué forma se debe resolver. Supongamos que tengo una clase "cursos". Si yo quisiera ...
  #1 (permalink)  
Antiguo 22/05/2006, 14:10
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
Duda de cómo hacer clases dependientes de otras.

Hola a todos, tengo la siguiente duda que no se de qué forma se debe resolver.

Supongamos que tengo una clase "cursos". Si yo quisiera recuperar los alumnos de ese curso, ¿cómo debo hacerlo? ¿hago que la clase cursos me devuelva todos los ID de los alumnos que lo integran y luego, en mi controlador, instancio los alumnos a partir de esos ID ó directamente devuelvo una instacia de "alumno" por alumno desde algún método de "curso"? Estaria muy agradecido de un ejemplo práctico, pues de seguro resolverá mi duda. ¿cómo seria la iteración?

Muchas gracias de antemano, saludos!
__________________
Sergio
  #2 (permalink)  
Antiguo 22/05/2006, 14:57
Avatar de adriancid  
Fecha de Ingreso: abril-2005
Ubicación: Versalles, Santiago de Cuba, Cuba
Mensajes: 53
Antigüedad: 19 años
Puntos: 0
Bueno eso depende de lo que desees hacer, yo haria lo siguiente

una clase alumno
una clase curso

ahora la herencia aqui es por composicion, una clase tiene alumnos por lo que entonces tendremos un arreglo en la clase curso de alumnos, ya entonces segun lo que necesites amigo, devuelves los alumnos uno por uno o el arreglo completo
  #3 (permalink)  
Antiguo 22/05/2006, 22:18
Avatar de enriqueplace  
Fecha de Ingreso: mayo-2005
Ubicación: Uruguay / Argentina
Mensajes: 1.102
Antigüedad: 19 años
Puntos: 32
¿"La herencia es por composición"?

¿Que quieres decir con eso?
__________________
Blog phpsenior.com Cursos a Distancia surforce.com
  #4 (permalink)  
Antiguo 23/05/2006, 06:33
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Como dice enrique no es herencia por composicion, sino composicion de objetos.

Es decir un curso tiene "n" alumnos, más bien "n" cursos tienen "m" alumnos.

Estamos trabajando con modelos asi que tendremos la clase cursos y la clase alumnos. La forma más fácil de hacerlo por composición seria la siguiente:

la clase alumno con todos los atributos privados y con sus metodos get y set

Código PHP:
class Alumno{
    
    private 
$id;
    private 
$nombre;
    private 
$apellidos;

    
    public function 
__construct($id,$nombre,$apellidos){
            
            
$this->nombre    $nombre;
            
$this->apellidos $apellidos;
            
$this->id        $id;
    }
    
    public function 
getNombre(){
        return 
$this->nombre;
    }
    
    public function 
setNombre($nombre){
        
$this->nombre $nombre;
    }
    
    public function 
getApellidos(){
        return 
$this->apellidos;    
    }
    
    public function 
setApellidos($apellidos){
        
$this->apellidos $apellidos;
    }
    
    public function 
getId(){
        return 
$this->id;
    }
    
    public function 
setId($id){
        
$this->id $id;
        
    }
    
    public function 
__toString(){
        return 
"$this->id .- Alumno: $this->nombre, $this->apellidos<br />";
    }

Bien ahora lo que necesitamos es una clase contenedora de alumnos jeje

Código PHP:
class Curso{
    
    public 
$alumnos;

    
    public function 
addAlumno(Alumno $alumno){
        
$this->alumnos[] = $alumno;
    }
    
    public function 
load($nombre){
        
        
$data simplexml_load_file("data.xml");

        
        foreach(
$data->{$nombre} as $registro){
            
            foreach (
$registro as $persona){
                
                
$alumno = new Alumno(
                                        
$persona->id,
                                        
$persona->nombre,
                                        
$persona->apellidos
                                    
);
                
$this->addAlumno($alumno);                
            }
        }
    }
    

Bueno pues ya está, he programado la clase para que pueda cargarse de dos formas: por xml, o "manualmente". Manualmente sería:
Código PHP:
echo "Carga manual<br />";
$curso_php = new Curso();

$curso_php->addAlumno(new Alumno(1,"Juan","Serrano"));
$curso_php->addAlumno(new Alumno(2,"Maria","Perez"));

foreach (
$curso_php->alumnos as $alumno){
    echo 
$alumno;

por xml sería:

Código PHP:
echo "Carga por xml <br />";

$curso_flash = new Curso();

// cargamos los alumnos con los datos del fichero xml
$curso_flash->load("flash");

// listado de alumnos inscritos al curso de diseño flash
foreach ($curso_flash->alumnos as $alumno){
    echo 
$alumno;

y el archivo xml de ejemplo sería:
Código PHP:
<?xml version="1.0" encoding="iso-8859-1"?>
<cursos>
 <flash>
   <alumno>
     <id>1</id>
     <nombre>Joan</nombre>
     <apellidos>Sarres</apellidos>
   </alumno>
   <alumno>
     <id>2</id>
     <nombre>Maria</nombre>
     <apellidos>Cap de Vila</apellidos>
   </alumno>
   <alumno>
     <id>2</id>
     <nombre>Pedro</nombre>
     <apellidos>Gomez</apellidos>
   </alumno>    
 </flash>
 <php>
   <alumno>
     <id>5</id>
     <nombre>Anna</nombre>
     <apellidos>Serra</apellidos>
   </alumno>
 </php>
</cursos>
Bueno espero que os sea de ayuda.

En verdad la propiedad publica de alumnos no estaría del todo correcta. Lo he puesto así por simpleza, normalmente cuando utilizo composicion más bien el patrón composite, implemento la interfaz iterator o arrayIterator de la SPL. Un tema algo avanzado.

Última edición por Casuis; 23/05/2006 a las 06:43
  #5 (permalink)  
Antiguo 23/05/2006, 18:18
 
Fecha de Ingreso: mayo-2005
Mensajes: 201
Antigüedad: 19 años
Puntos: 2
para la perte del tema avanzado, una liga que habla sobre el patron iterador
http://agamenon.uniandes.edu.co/~pfi.../Iterator.html
__________________
Saludos!
Mty-NL..
  #6 (permalink)  
Antiguo 24/05/2006, 02:59
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Bueno por si alguien está interesado saber que el patrón Iterator ya está implementado con PHP5, ya viene integrado. Para que sirve? Básicamente para poder iterar un objeto como si fuera un array. Esto nos viene de perlas en el ejemplo para evitar el atributo "public" <-- soy un purista, como dicen los de wordpress
Cita:
code is poetry
(por cierto que chulada de ajax que lleva el nuevo wordpress jiji) lo que haremos será hacer la clase cursos iterable obteniendo el alumno en cada iteración.

Intentaré explicarlo como si fuera la primera vez que lo hice.
1) Vamos a la documentación PHP5 Iterator y leemos un poco el arbol de herencia etc... Bueno lo importante es saber que es una interface lo que significa que debemos especificar <nombre_clase> implements Iterator.
2) Implementar los cinco métodos abstractos que nos obliga la interface.
3) y ya está !!! La verdad SPL es una maravilla.

Pues ya esta. Para modificar la clase Cursos tendremos que hacer lo siguiente:

pasamos el atributo alumnos de public a private

Añadimos un atributo puntero para saber la posicion del array de objetos alumnos extendemos de Iterator y declaramos los cinco métodos que nos obliga la interface.
Que son los siguientes (todos son abstactos):
current ()
key ()
next ()
rewind ()
valid ()


Explicación de los metodos:
current() : devolverá el contenido actual de la posicion del array
key() : devolverá el numero de posición del array
next() : pasará a la siguiente posición.
rewind() : ponemos a cero la posición.
valid() : comprobaremos que no se produzca desbordamiento.

Recordamos que en el ejemplo nuestro array es $alumnos que es un array de objetos.

Quedaría así:
Código PHP:
class Curso implements Iterator{
    
    private 
$alumnos;
    private 
$puntero;

    
    public function 
addAlumno(Alumno $alumno){
        
$this->alumnos[] = $alumno;
    }
     public function 
current (){

     }
     public function 
key (){

     }
     public function 
next (){

     }
     public function 
rewind (){

     }
     public function 
valid (){

     }
    
    public function 
load($nombre){
        
        
$data simplexml_load_file("data.xml");

        
        foreach(
$data->{$nombre} as $registro){
            
            foreach (
$registro as $persona){
                
                
$alumno = new Alumno(
                                        
$persona->id,
                                        
$persona->nombre,
                                        
$persona->apellidos
                                    
);
                
$this->addAlumno($alumno);                
            }
        }
    }
    

Ahora rellenamos los métodos de la interface Iterator, por cierto he añadido un nuevo método count() para saber el número total de alumnos:
Código PHP:
class Curso implements Iterator{
    
    private 
$alumnos;
    private 
$puntero;

    
    public function 
addAlumno(Alumno $alumno){
        
$this->alumnos[] = $alumno;
    }
     public function 
current (){
         if(!
$this->valid())
             return 
false;
         if(empty(
$this->alumnos[$this->puntero]))
             return array();
         return 
$this->alumnos[$this->puntero];
     }
     public function 
key (){
         return 
$this->puntero;
     }
     public function 
next (){
         return ++
$this->puntero;
     }
     public function 
rewind (){
         
$this->puntero 0;
     }
     public function 
valid (){
         return 
$this->puntero $this->count();
     }
     public function 
count(){
         return 
count($this->alumnos);
     }
    
    public function 
load($nombre){
        
        
$data simplexml_load_file("data.xml");

        
        foreach(
$data->{$nombre} as $registro){
            
            foreach (
$registro as $persona){
                
                
$alumno = new Alumno(
                                        
$persona->id,
                                        
$persona->nombre,
                                        
$persona->apellidos
                                    
);
                
$this->addAlumno($alumno);                
            }
        }
    }
    

Ahora podemos iterar el objeto Curso como si fuera un array:
Código PHP:
echo "Carga por xml <br />";

$curso_flash = new Curso();
$curso_flash->load("flash");

foreach (
$curso_flash as $alumno){
    echo 
$alumno;


Última edición por Casuis; 24/05/2006 a las 03:17
  #7 (permalink)  
Antiguo 24/05/2006, 05:27
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Bueno ya que estoy lanzado pasaremos a la última fase. Yo la llamo pureza del código.

La clase Alumno es funcional, pero mirandola detenidamente podemos ver dos cosas.
1) Por cada propiedad tenemos que hacer dos metodos, uno get y otro set.
2) Una vez instanciado el objeto como podemos acceder a las propiedades sin programar estas funciones y sin poner los atributos a public.

Pues bien vamos a optimizar alrededor de un 60% el codigo de la clase Alumno.

En php5 existen las nuevas funcionalidades llamadas métodos mágicos. Vamos a aplicar 2 de ellos el __get y el __set. Con estos dos métodos podemos eliminar todos los metodos existentes tipo get y set referidos a cada propiedad de la clase. Para aplicarlo eliminaremos todas las variables y declararemos dentro de un array asociativo. Así eliminaremos un 60% del código y añadiremos el doble de simpleza.

Bien recordemos que el código anterior para Alumnos era el siguiente:

Código PHP:
class Alumno{
    
    private 
$id;
    private 
$nombre;
    private 
$apellidos;

    
    public function 
__construct($id,$nombre,$apellidos){
            
            
$this->nombre    $nombre;
            
$this->apellidos $apellidos;
            
$this->id        $id;
    }
    
    public function 
getNombre(){
        return 
$this->nombre;
    }
    
    public function 
setNombre($nombre){
        
$this->nombre $nombre;
    }
    
    public function 
getApellidos(){
        return 
$this->apellidos;    
    }
    
    public function 
setApellidos($apellidos){
        
$this->apellidos $apellidos;
    }
    
    public function 
getId(){
        return 
$this->identificador;
    }
    
    public function 
setId($id){
        
$this->id $id;
        
    }
    
    public function 
__toString(){
        return 
"$this->id .- Alumno: $this->nombre, $this->apellidos<br />";
    }

Así el nuevo código quedaria de la siguiente manera:
Código PHP:
class Alumno{
    
    private 
$campos = array();


    
    public function 
__construct($id,$nombre,$apellidos){
        
        
$this->campos["id"]        = $id;
        
$this->campos["nombre"]    = $nombre;
        
$this->campos["apellidos"] = $apellidos;
    }
    
    public function 
__get($propiedad){
        
        if(!
array_key_exists($propiedad,$this->campos))
            throw new 
Exception("Esta propiedad no existe $propiedad");
        return 
$this->campos[$propiedad];
    }
    
    public function 
__set($propiedad$valor){
            
        if(!
array_key_exists($propiedad,$this->campos))
            throw new 
Exception("Esta propiedad no existe $propiedad");
        
$this->campos[$propiedad] = $valor;
    }
    
    public function 
__toString(){
        return 
"$this->id .- Alumno: $this->nombre, $this->apellidos<br />";
    }

Ahora podemos acceder a las propiedades de los Alumnos de la siguiente manera:
Código PHP:
echo "Carga por xml <br />";

$curso_flash = new Curso();
$curso_flash->load("flash");

foreach (
$curso_flash as $alumno){
    echo 
$alumno->id;
    echo 
$alumno->nombre;
    echo 
$alumno->apellidos;

Bueno por estas cositas son por las que digo que php5 le pasa la mano a php4. En estas dos clases he utilizado nuevas funcionalidades solo disponibles para la versión 5. Que son utilización de simple_xml, métodos mágicos __toString, __get, __set, type hinting (PHP 5 introduce Type Hinting. Las funciones ahora son capaces de forzar que los parámetros sean objetos especificando el nombre de la clase en el prototipo de la función. )
Código PHP:
    public function addAlumno(Alumno $alumno){
        
$this->alumnos[] = $alumno;
    } 
excepciones throw new Exception, utilización de SPL con la interfaz Iterator ...
Mi conclusión es que la versión 5 está preparada para soluciones empresariales de gran proyección, también sus características son más moldeables a la hora de construir frameworks.

Supongo que la gente no lo utiliza ya que estan acostumbradas a la antigua versión de php4. Por suerte a la gente que llegamos de Java nos ha sido una bendición la nueva versión :). Saludos a todos

Última edición por Casuis; 24/05/2006 a las 05:49
  #8 (permalink)  
Antiguo 26/05/2006, 13:21
 
Fecha de Ingreso: abril-2006
Mensajes: 62
Antigüedad: 18 años
Puntos: 0
Pregunta muy buenos hints

Gracias Casuis, tus comentarios y ejemplos me resultaron de mucha utilidad

Ahora te hago una pregunta sobre el último ejemplo, donde reescribís la clase Alumnos haciendo uso de los métodos mágicos _set y __get.

¿Te parece que ese esquema es útil solamente cuando no hay validación de los datos que se pasan?

Por ejemplo si quisiera asegurarme, dentro de la clase Alumno, que el valor que me pasan para "id" es un entero, y que los valores para "nombre" y "apellido" sean cadenas y no excedan los 50 caracteres.
¿Debería utilizar el primer esquema, es decir "function setNombre" y etc.?
¿O hay una manera elegante de resolverlo conservando los métodos mágicos?

Y en general: ¿es bueno poner validación de este tipo dentro de las clases o es signo de alguna mala práctica?
__________________
Guish
  #9 (permalink)  
Antiguo 27/05/2006, 11:23
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Imaginate que tienes un formulario donde das de alta un alumno, este formulario llama a procesar_alumno.php. Bien pues antes de pasarlo a alumno efectivamente tenemos que validar los datos del post.
Código PHP:
function alta_alumno(){
    
$alumno = new Alumno();
    
    if(
is_string($_POST["nombre"]) && strlen($_POST["nombre"])<50){
        
        
$alumno->nombre htmlentities($_POST["nombre"],ENT_QUOTES);
    }
    if(
is_string($_POST["apellidos"]) && strlen($_POST["apellidos"])<50){
        
        
$alumno->apellidos htmlentities($_POST["apellidos"],ENT_QUOTES);
    }
    
// como todo es correcto guardamos
    
$id $alumno->save(); //devolverá el id autogenerado
    


Última edición por Casuis; 27/05/2006 a las 11:31
  #10 (permalink)  
Antiguo 31/05/2006, 23:36
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
Muchas gracias a todos, la verdad que he aprendido muchisimo con lo expuesto!

Casuis: Lástima que tengo que utilizar PHP4 sí o sí porque el servidor en el que alojo mis páginas no soportan PHP5, así que lo haré chapada a la antigua y de la forma no tan pura (propiedades públicas). Como verás, recién estoy inicializandome en el mundo de la POO (no se inglés, he leido lo que pude en español), y estoy fascinado! De todas formas dejaré los detalles de PHP 5 para mi próximo proyectillo.

Sobre el ejemplo de validación que diste, las cuales parecen estar en una página independiente. ¿Qué pasaria si hay varias paginas en donde queremos dar de alta un usuario? ¿Validamos en cada página o conviene validar las propiedadades en el mismo abjeto al llamar al método "alta"?

Otra consulta: ¿Cómo seria la forma correcta de traer mi listado de cursos? ¿Una clase Cursos con un método load que instancie los cursos en un array (análogo al ejemplo de Curso-Alumno)?

Muchas gracias por tu generosidad!
__________________
Sergio
  #11 (permalink)  
Antiguo 02/06/2006, 01:43
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
Hola nuevamente:

Viendo que con Iterator en PHP5 las cosas estan más puras (no sé que significa esto, pero así dice Casuis ) y simples, me tomé el atrevimiento de hacer una implementación casera de este patrón en PHP4 que quiero compartir para saber si la misma está interpretada correctamente.

Debido a las limitaciones de la versión que utilizo, he tenido que hacer la siguiente clase:

Código PHP:
<?php

class Iterador {

    var 
$_matriz;
    var 
$_puntero;

    function 
iterador(& $matriz) {
        if(!
is_array($matriz))
            return 
false;
            
        
$this->_matriz =& $matriz;
        
$this->reajustar();
        
        return 
true;
    }

    function & 
iterar($devolver_al_terminar false) {
        
$this->_puntero++;
        if(isset(
$this->_matriz[$this->_puntero]))
            return 
$this->_matriz[$this->_puntero];
        else
            
$this->reajustar();
        
        return 
$devolver_al_terminar;
    }
    
    function 
reajustar() {
        
$this->_puntero = -1;
    }
    
}
?>
Está en su mínima expresión, se le pueden agregar un par de métodos más en caso de ser necesario.

Siguiendo el ejemplo de Casuis de Curso-Alumno:

Clase Alumno:
Código PHP:
<?php

class Alumno {

    var 
$_id;
    var 
$_nombre;
    
    function 
alumno($id$nombre) {
        
$this->_id $id;
        
$this->_nombre $nombre;
    }
    
    function 
getId() {
        return 
$this->_id;
    }
    
    function 
getNombre() {
        return 
$this->_nombre;
    }
    
    function 
setNombre($valor) {
        
$this->_nombre $valor;
    }

}
?>
No sufre modificaciones con respecto a la anterior, pero la he achicado un poquito para hacer las pruebas.

Clase Curso:
Código PHP:
<?php

class Curso {

    var 
$_nombre;
    var 
$_alumnos;
    
    var 
$_iterador_alumno;
    
    function 
curso($nombre) {
        
$this->_nombre $nombre;
    }
    
    function 
getNombre() {
        return 
$this->_nombre;
    }

    function 
addAlumno(& $alumno) {
        
$this->_alumnos[] =& $alumno;
    }

    function & 
getAlumnos() {
        if(empty(
$this->_iterador_alumno))
            
$this->_iterador_alumno =& new Iterador($this->_alumnos);

        return 
$this->_iterador_alumno->iterar();
    }
        
}
?>
Las modificaciones son:
  • El "&" al argumento del método addAlumno, para mantener la refencia y
  • el método getAlumnos el cual hace uso de una instancia de Iterador en la propiedad _iterador_alumno.

Quedaría:
Código PHP:
<?php

$alumno_1 
=& new Alumno(1'Sergio');
$alumno_2 =& new Alumno(2'Cesar');

$curso_php =& new Curso('PHP');
$curso_php->addAlumno($alumno_1);
$curso_php->addAlumno($alumno_2);

while(
$alumno =& $curso_php->getAlumnos())
    echo 
$alumno->getNombre();

?>
Las referencias están perfectas, pues hacer la prueba de cambiar los nombres en el bucle y luego corroborar en $alumno_1 y $alumno_2.

Algo que me parece que está mal es que no puedo personalizar la iteración de una clase específica, ya que todo el control es dado a la clase Iterador. ¿Esto esta mal, no? ¿Deberia hacer una implementación interna de Iterator en cada clase que necesite esta funcionalidad?

Lecturas de inspiración :
Muchas gracias! Saludos.
__________________
Sergio
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




La zona horaria es GMT -6. Ahora son las 20:40.