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

Mi experimento MVC

Estas en el tema de Mi experimento MVC en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Hola a todos! Nuevamente molestando, pero la verdad que esto de POO me genera tal incertidumbre (sobre si hago las cosas bien o mal) que ...
  #1 (permalink)  
Antiguo 01/06/2006, 01:47
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
Mi experimento MVC

Hola a todos! Nuevamente molestando, pero la verdad que esto de POO me genera tal incertidumbre (sobre si hago las cosas bien o mal) que no me deja dormir, debido a mi falta de teoria y experencia.

Antes debo aclarar que este experimento lo estoy desarrollando en PHP4, siendo conciente de los límites que esto conlleva. Digo experimento porque en verdad no tengo idea de lo que estoy haciendo, sólo la inercia me lleva para adelante con la esperanza que algo bueno puede salir de aquí (algo bueno podria ser saber que está todo mal ).

Empezando, les comento que estoy tratando de seguir las lineas de un patrón (no tengo muy claro el concepto de patrón) MVC. Para ello he definido la siguiente estructura de clases:

Aplicacion
Es la clase principal, se instancia por única vez en la página que hará de acceso al sistema. Se encarga (al crearse) de recojer las variables GET que indican el controlador (clase) que se instanciará, como así también la acción (método) que se llamará en una petición. Seguidamente y como paso final se hace dicha tarea (instanciar el controlador y llamar el método).

Controlador
Es la clase abstracta (no puedo creer que utilice esa palabra, antes de ayer he aprendido lo que significa) que sirve para dar las bases a las clases concretas de los controladores que se instanciarán. Por ahora, está compuesta por las siguientes clases (se instancian en propiedades públicas (no queda otra) en el constuctor del mismo):

UsuarioActual
Realiza una instacia del usuario que ha iniciado sesión en el sistema, sino hay, establece una propiedad para definir un usuario anónimo. Esta clase deberia ¿extender? a la clase Usuario del modelo, pero como esta última ¿extiende? a una clase abstracta Modelo y PHP4 no permite herencia múltiple, la clase UsuarioActual pasa a estar compuesta por una de Usuario. Luego de que esté creado este objeto, el controlador revisa (a través de un método del mismo) si el tipo de usuario tiene permisos suficientes como para acceder a éste.

Vista
Se encarga de imprimir la vista del controlador. Tiene propiedades como "disposicion" (vendria a ser el layout) y "plantilla" (la vista del controlador), entre otras, por ejemplo que son arrays de archivos css y js a incluir. El método más importante lo llamé "mostrar()", que hace eso mismo, y es llamado desde las acciones del controlador (en caso de ser necesario). Cuando se llama a este método, en caso de no haber establecido la propiedad "plantilla", buscará una plantilla con el mismo nombre del controlador (cosas de vagos, jeje). También dispone de un método que permite incluir las variables que se "pasaran" desde el controlador para mostrar en la plantilla.

Sessiones
Se encarga de "aislar" la administración de sessiones, por si mas adelante se las quiere administrar desde una bd, etc.

Fechas
Para manejar las fechas en GTM y luego aplicar diferencias horarios según configuraciones, etc.

Y otras clases de este tipo...

Modelo
Clase abstracta para dar base a las clases del modelo. Hace la conexión a la bd (instanciando otra clase, por ejemplo "Mysql", "Access", etc. indicada a partir de una constante en un archivo de configuración) y haciendo de "mascara" a la misma (¿A eso se le llama interfase?). Tiene métodos como "ejecutar(sql)", etc. También dispuse de un metodo llamado "usar" (que puede llamarse Metodo::usar('nombre_de_clase_modelo')) para incluir segun sea necesario los archivos de las clases de modelo. El id de la conexión lo puse en una variable global y la consulto en cada instancia para usar siempre la misma conexión.

Ayuditas
Esta clase no hace falta instanciarla, el objetivo de la misma es agrupar las funciones de "ayuda". Por ejemplo contiene funciones para utlizar tanto en el controlador (redirigir, validaciones, etc.) como para la vista (enlaces, grillas, etc.).

------------------------------------------------------------

Como les anticipé, esto es menjunje experimental. Prometo publicar código luego, pero antes quiero saber si está bien planteado. Algo que me llamó la atención es que me parece que abusé de la funcionalidad de los constructores, pues desde allí llamo a la mayoria de tareas que deben realizarse automaticamente. ¿Esto está bien o debo instanciar y luego llamar un método que haga estas tareas?

Otra cosa, tengo un archivo de configuración que se compone de definición de constantes. ¿Está bien o deberia crear una clase Config que tenga propiedades de las configuraciones del sistema de manera que todo sean objetos?

También veo que estas clases se instancian una sola vez. ¿Esta bien esto o conviene no hacer instancia de las mismas y utilizar sus funciones a través de "::"?

------------------------------------------------------------

Un ejemplo de como se ejecutaria una acción:

Una clase del modelo: Usuario.php
Código PHP:
<?php

class Usuario extends Modelo {

    var 
$nombre;

    function 
usuario($id) { parent::modelo();
    
        
$this->bd->ejecutar("select nombre from usuarios where id = '$id'");

        
$fila $this->bd->filas();
        
        
$this->nombre $fila['nombre'];
    }
    
    function 
getNombre() {
        return 
$this->nombre;
    }

    function 
guardar() {
        
//...
    
}

}

?>
Un controlador: MostrarNombreUsuario.php
Código PHP:
class MostrarNombreUsuario extends Controlador {

    function 
mostrarNombreUsuario() { parent::controlador(); }
    
    function 
inicial() {
        
        
Modelo::usar('usuario');
        
$usuario = new Usuario(1);
        
        
$this->vista->agregarVariable('nombre'$usuario->nombre);
        
$this->vista->mostrar();
        
    }


La vista: MostrarNombreUsuario/inicial.php
Código PHP:
<h2>Mostrar nombres</h2>

<p>El nombre del usuario es <?= $this->nombre ?></p>
---------------------------------------------------

Una url de ejemplo para ejecutar esto seria:
http://host/index.php?mod=MostrarNombreUsuario

Como no se especifica la acción a ejecutar del controlador, se busca la acción "inicial" por defecto, sino existe, tira error.

---------------------------------------------------

Generalmente no soy de escribir posts largos, asi que ya estoy medio perdido, pero en fin, esto es a grandes rasgos lo que hice. ¿Sirve? En caso que no esté todo absolutamente mal, ¿qué deberia corregir?. En caso que esté todo mal, ¿en qué le estoy errando?

También debo aclarar que he leido sobre RubyOnRails, el cual me ha dado la idea de como organizarme, como así también parte de la estructura de carpetas.

Muchas gracias a todos por vuestra atención, un abrazo!
__________________
Sergio
  #2 (permalink)  
Antiguo 01/06/2006, 01:56
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
He leido mi post y admito que me da verguenza !!

Espero sepan comprenderme, de alguna forma tengo aprender y de esta forma lo estoy intentando.

Ta luego...
__________________
Sergio
  #3 (permalink)  
Antiguo 01/06/2006, 04:22
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Cita:
He leido mi post y admito que me da verguenza !!

Espero sepan comprenderme, de alguna forma tengo aprender y de esta forma lo estoy intentando.

Ta luego...
Hola Hereje, primero de todo felicitarte !!! para empezar en esto de la POO la verdad es que te estas desenvolviendo de una manera genial, debes haber leído bastante para llegar a hacer esta aproximación. Sobre todo no te desanimes. Te puedo decir que muchos envidiarian tu percepción, incluído yo mismo jejeje que tanto me ha costado.

Primero decirte que la forma en que se implementa un MVC puede variar hasta el infinito, se intentan especificar unas bases eso sí (patrones, estilos de codificación) pero la forma en que lo estructuras puede llegar a ser muy personal (mira la de frameworks diferentes que hay).

Yo te recomendaria empezar por los 3 pilares básicos. Las 3 clases abstractas principales, modelo, vista, y controlador.
Te propongo algo: empieza programando el modelo siguiendo el patrón ActiveRecord. Intenta representar un objeto como si fuera una tabla.

programa una clase que principalmente tenga las siguientes funciones:

function find($id); <-- devolverá un registro de la tabla indicada
funcion find_all($condiciones) <-- devolverá una colección de registros que cumplan las condiciones

function update($id) <--- actualizará los registros
function update_all($id) <-- actualizará una colección de registros que cumplan las condiciones
function save($id) <-- guardará el registro
funcion select($id) <-- seleccionará el registro

También te recomiendo tener una variables minimas:
primaryKey: guardará la clave primaria (te aconsejo que siempre que puedas sea id autonumerica)
tableName : nombre de tabla
fields: nombre de los campos y su contenido inicialmente seteados.
db : donde guardarás la conexion a la base de datos.
  #4 (permalink)  
Antiguo 01/06/2006, 05:33
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Bueno va te voy a ayudar un poquillo jejeje. Voy a hacerte la clase model con unas cuantas funciones para que veas como va el tema.
Voy a utilizar adodb para optimizar un 400% las linias de código.

Estructura del miniproyecto:
+miniprueba
+adodb
- Model.php
- Mensajes.php
- index.php

la carpeta adodb seran las librerias adodb

te pongo la tabla mensajes
Cita:
CREATE TABLE `mensajes` (
`id` int(11) NOT NULL auto_increment,
`titulo` varchar(255) NOT NULL,
`mensaje` NOT NULL,
`fecha` datetime NOT NULL,
`autor` varchar(150) NOT NULL,
PRIMARY KEY (`id`)
) ;



INSERT INTO `mensajes` VALUES ('Tercer mensaje', 'Nuevo mensaje', '2006-05-01 16:55:29', 'Pepe');
INSERT INTO `mensajes` VALUES ('Tercer mensaje', 'Nuevo mensaje', '2006-05-01 17:00:39', 'Pablo');
Model.php (abstracto del que heredaran) los modelos

Código PHP:
require('adodb/adodb.inc.php');

class 
Model{
    
    var 
$tableName;
    var 
$fields;
    var 
$primaryKey;
    var 
$db;
    
    function 
Model(){
        
// obtenemos el nombre de la clase 
        
$this->tableName strtolower(get_class($this));
        
//esto normalmente se coje de la configuracion !!!
        //$dsn = 'mysql://user:pwd@localhost/mydb';
        
$dsn 'mysql://casuis:casuis@localhost/forosweb_development';
        
// creamos objeto adodb <-- tenemos que comprobar si hay error
        
$this->db ADONewConnection($dsn);
        
// obtenemos todos los campos en tiempo de ejecucion
        
$fields $this->db->MetaColumnNames($this->tableName);
        
$keys array_values($fields);
        foreach(
$keys as $fieldName){
            
$this->fields[$fieldName] = "";
        }    
        
//obtenemos un array para conseguir primarykey
        
$id $this->db->MetaPrimaryKeys($this->tableName);
        
// la primaryKey será la primera posición del array
        
$this->primaryKey array_shift($id);
    }
    
    function 
find($id){
        
$sql "select * from ".$this->tableName" where ".$this->primaryKey "=".intval($id);
        
$sql "select * from "$this->_table " where ".$this->_primaryKey ."=".intval($id);
        
$this->db->SetFetchMode(ADODB_FETCH_ASSOC); // asi tenemos un array asociativo
        
$rs $this->db->GetRow($sql);
        return 
$rs;        
    }
    function 
find_all($fields="*",$where=""$order=""){
        
        if(!
strlen($fields)) 
            
$fields "*";

        
$sql "select ".$fields " from "$this->tableName " where 1=1";
        
        if(
strlen($where)) 
            
$sql .= " AND ".$where;

        if(
strlen($order)) 
            
$sql .= " ORDER BY ".$order;
        
//echo $sql; exit; // solamente para debug
        
$this->db->SetFetchMode(ADODB_FETCH_ASSOC); // asi tenemos un array asociativo
        
$rs $this->db->GetArray($sql);
        return 
$rs;

    }
    
    function 
select($id){
        
        
$sql "select * from ".$this->_table ."where " $this->_primaryKey intval($id);
        
$rs $this->db->Execute($sql);
        
$this->fields $rs->Fields();
        
    }
    function 
delete($id){
        
$sql "delete from ".$this->_table ." where ".$this->_primaryKey ."=".$id;
        
$this->db->Execute($sql);
    }
    function 
getFields(){
        return 
$this->fields;
    }
    function 
setFields($fields){
        
$this->fields $fields;
    }

    function 
save($rs=""){
        if(!
strlen($rs))
            
$insertSQL $this->db->GetInsertSQL($this->tableName$this->fields);
        else 
            
$insertSQL $this->db->GetInsertSQL($this->tableName$rs);
            
//echo $insertSQL; exit; //solamente debug
        
if(!$this->db->Execute($insertSQL)){
            echo 
"error insertando:".$this->db->ErrorMsg();
            die();
        }else{
            
//retornaremos la id autogenerada
            
return $this->db->Insert_ID();
        }

    }

Mensajes.php
Código PHP:
require_once("Model.php");
class 
Mensajes extends Model{} 
Index.php será para hacer las pruebas por ejemplo seleccionamos todos los campos
Código PHP:
require_once("Mensajes.php");

$mensajes = new Mensajes();
$rs $mensajes->find_all();
foreach(
$rs as $row){
    echo 
$row['id']. "<br />";
    echo 
$row['titulo']. "<br />";
    echo 
$row['autor']. "<br />";

Seleccionamos todos los mensajes con condiciones:
Código PHP:
$mensajes = new Mensajes();
$where "id >2";
$order "autor";
$rs $mensajes->find_all("",$where,$order);

foreach(
$rs as $row){
    echo 
$row['id']. "<br />";
    echo 
$row['titulo']. "<br />";
    echo 
$row['autor']. "<br />";

y por ejemplo insertamos un nuevo campo
Código PHP:
$mensajes = new Mensajes();

$rs $mensajes->getFields();

$rs['titulo'] = "hola q tal";
$rs['mensaje'] = "mensajes insertado";
$rs['autor'] = "casuis";
$rs['fecha'] = date("Y-m-d");
$id $mensajes->save($rs);
echo 
$id
Fácil no? bueno te faltan implementar algunas funciones pero lo dejo a tu manera tambien puedes poner de informacion como getTableName o setTableName etc... Es un comienzo solo nos falta el controlador y la vista. De hecho se puede hacer un MVC en unos minutillos pero esos secretos estan muy bien guardados ajajaja
Suerte

Por cierto en la linia
Código PHP:
$this->db ADONewConnection($dsn); 
no estaria del todo bien ya que cada modelo crearia una nueva instancia de adodb
Para evitar esto tendriamos que hacer
Código PHP:
$this->db dataManager::getInstance(); 
donde dataManager es una clase que implementa a mi amigo "singleton" que se asegurará de devolver una única instancia de adodb

Última edición por Casuis; 01/06/2006 a las 05:46
  #5 (permalink)  
Antiguo 01/06/2006, 10:51
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 Casuis !!!

Un gusto para mi que hayas respondido este mensaje! Yo sabia que tenia bastante floja la capa del modelo, pero ahora con esta forma que propones y la mano caida del cielo ADODB, todo va ir mejor.

Cita:
Es un comienzo solo nos falta el controlador y la vista. De hecho se puede hacer un MVC en unos minutillos pero esos secretos estan muy bien guardados ajajaja
Casi me largo a llorar cuando leo esto, pero te respeto, supongo que eso que debes tener guardado es producto de largas investigaciones y horas de trabajo y experiencia. Es entendible tu postura y una lástima para mi.

El problema es que tengo poco tiempo para terminar un trabajo y estoy haciendo estos experimentos por aquí, necesitaria saber, una vez implementado estos cambios en el modelo, el resto, a nivel general, esta mas o menos como para seguir?

Un gran abrazo y nuevamente agredecido!
__________________
Sergio
  #6 (permalink)  
Antiguo 01/06/2006, 12:10
 
Fecha de Ingreso: abril-2006
Mensajes: 62
Antigüedad: 18 años
Puntos: 0
De acuerdo

Hola Hereje! También quiero felicitarte, empezar a trabajar sobre esto ya te da suficiente mérito

Debo anticiparte que yo estoy en la misma cruzada, tratando aprovechar de la mejor manera este patrón.

Leí atentamente tu post, incluyendo el código pero no conseguí hacerme una idea precisa sobre cómo funciona, en parte por mi falta de experiencia. Me quedo vacío sobre todo lo referente a la clase Modelo. A grandes rasgos sí parece seguir un MVC, pero que te diga eso no te sirve de ayuda

Pero, si el reloj me deja margen, voy a inspeccionar a fondo tu implementación cuando publiques el resto del código.

También me interesa mucho saber que estuviste leyendo al respecto y, si querés y podés, dejalo en el tema que cree al respecto.
__________________
Guish
  #7 (permalink)  
Antiguo 01/06/2006, 15:06
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Cita:
El problema es que tengo poco tiempo para terminar un trabajo y estoy haciendo estos experimentos por aquí, necesitaria saber, una vez implementado estos cambios en el modelo, el resto, a nivel general, esta mas o menos como para seguir?
Por cuestión de tiempo te va a llevar y mucho si recien entras en la POO y los modelos de programación. Quizá no te debería haber dicho que en unos minutillos se consigue un MVC es incierto. Si te proporcionara todo el código no te serviría de nada. Entonces por qué no coger un framework y destriparle el código¿? Te he hechado un cable y te ayudaré en lo que pueda pero vas a tener que sacartelo tu mismo.
Podrán pasar dos cosas:
1) Que abandones.
2) Que lo itentes sacar. Parece que has leído mucho pero te falta picar código para completar toda la fase.

Si no te ayudo más es porque con la práctica es cuando realmente lo aprenderás. Estoy seguro que lo vas a sacar es questión de insistencia.

Aunque ya sabes primero pruebalo...

Ánimo vas muy bien encaminado.

Intenta programar la vista si ves que no te sale miramos el código y te doy soluciones. También podemos optar por aprobecharnos de una libreria hecha ;)
  #8 (permalink)  
Antiguo 01/06/2006, 15:16
Avatar de Hereje  
Fecha de Ingreso: junio-2002
Ubicación: Córdoba, Argentina
Mensajes: 439
Antigüedad: 21 años, 10 meses
Puntos: 2
Tienes toda la razon Casius, mantendré al tanto sobre novedades.

Saludos y gracias nuevamente!

PD// La vista ya la tengo programada, deseas que muestre el código ?

[edit]
Sé que el & sirve para pasar por referencia. Pero no entiendo como funciona aquí:
$obj =& new Obj;
[/edit]
__________________
Sergio
  #9 (permalink)  
Antiguo 01/06/2006, 15:26
 
Fecha de Ingreso: septiembre-2005
Mensajes: 142
Antigüedad: 18 años, 7 meses
Puntos: 3
Bueno... mejor te dejo un enlace
http://www.eslomas.com/index.php/arc...jetos-en-php4/

ya verás es genial vale la pena leerlo. Además para los que ya lo saben valor/referencia va bien pegarle un repaso. Por ejemplo en C un puntero guarda la dirección de memoria pero como lo hace PHP ??? <<- devuelve un alias. Estas pequeñas perlas las encontrarás en el artículo y con ejemplos.

Espero que te sirva de ayuda.
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 19:27.