Estructura Modular de Directorios

Por zsamer en Mayo 10, 2008

La semana pasada escribí un articulo sobre un Sitema de Login el cual continuaba con la linea del set de artículos Comenzando con Zend Framework con el fin de complementar la introducción y comenzar a utilizar todo el potencial del Framework implementando Autenticación de Usuarios. En aquel post vimos cómo crear un escenario simple en el que creamos un sitio que permitía al usuario hacer sesión para poder escribir comentarios. Cubrimos los conceptos de alto nivel que hay detrás de MVC junto con un practico ejemplo de lógica de negocio de autenticación.

En el post de hoy vamos a hacer un refactoring de nuestra aplicación de manera que tengamos una estructura modular de los controladores permitiendo organizar de mejor forma nuestro sistema.

La estructura de directorio modular permite separar diferentes MVC en aplicaciones individuales y re-usar estas con diferentes Front Controllers.

Para ilustrar la estructura de directorios:
Figura Nº1
Estructura Modular de Directorios

 

Como observación dentro de cada modulo tenemos carpetas MVC, models, views, controllers, sin embargo por comodidad personal todos los modelos de los distintos modulos los voy a dejar dentro de la carpeta models en el directorio \quickstart\application\models y las voy a separar por directorios, con el nombre del modulo correspondiente. Aunque también se podrían dejar los modelos de los modulos en la carpeta models dentro de cada modulo.

Cuando se trabaja con Módulos hay que especificar en el nombre de la clase controladora el NameSpace (mombre del modulo) al cual pertenece seguido de un guión bajo y el nombre del controlador:

Nomenclatura: Nombremodulo_NombrecontroladorController (NameSpace_NameController)

Ejemplo de la clase controladora Index del modulo User (IndexController.php):

PHP:
  1. class User_IndexController extends Zend_Controller_Action {
  2. public function indexAction() {}
  3. }

NO se agrega el prefijo namespacing en los controladores del módulo default

Hay que tener en cuenta que en el módulo default, los controladores no necesitan un prefijo de nombre del modulo o namespacing. Así, en el ejemplo anterior, los controladores por defecto en el módulo no es necesario un prefijo de 'Default_' - se trata simplemente con el nombre del controlador: «IndexController, ErrorController 'y' FooController». No obstante el prefijo namespacing se utiliza en todos los otros módulos.

Hasta ahora hemos hecho un completo análisis teórico de la Estructura Modular de Directorios, pero ¿cómo funciona esto realmente?

Con esta nueva estructura van a cambiar todas las URL par ejecutar la petición correcta en el modulo, controlador y acción correspondiente:

CODE:
  1. :module/:controller/:action/
  2. http://example.com/user/index/index
  3. http://example.com/user/index/
  4. http://example.com/user/

Entrando más en profundidad con nuestro ejemplos tenemos un nuevo index.php o bootstrap quedaría de la siguiente forma:

quickstart/public/index.php

PHP:
  1. <?php
  2. /** Bootstrap */
  3. /** Configuración de manejo de error */
  4. error_reporting(E_ALL|E_STRICT);
  5. date_default_timezone_set("America/Santiago");
  6.  
  7. /** Configuración de Directorios */
  8. $rootPath = dirname(dirname(__FILE__));
  9. set_include_path(get_include_path() . PATH_SEPARATOR .
  10.                  $rootPath . '/application/config' . PATH_SEPARATOR .
  11.                  $rootPath . '/application/models' . PATH_SEPARATOR .
  12.                  $rootPath . '/library' . PATH_SEPARATOR .
  13.                  $rootPath . '/public');
  14.  
  15. /** Nuevo Autocarga de clases de Zend FrameWork */        
  16. require_once 'Zend/Loader.php';
  17. Zend_Loader::registerAutoload();
  18.  
  19. /** Carga el archivo de configuración ini */
  20. require_once 'Zend/Config/Ini.php';
  21. $config = new Zend_Config_Ini('quickstart.ini', 'default');
  22.  
  23. /** Inicializa y conecta Base de Datos */
  24. require_once 'Zend/Db.php';
  25. require_once 'Zend/Db/Table/Abstract.php';
  26. $db = Zend_Db::factory($config->database);
  27. Zend_Db_Table_Abstract::setDefaultAdapter($db);
  28.  
  29. /** Inicializa layout */
  30. $options = array(
  31.     'layout'     => 'Main',
  32.     'layoutPath' => '../application/layouts/scripts/'
  33. );
  34.  
  35. require_once 'Zend/Layout.php';
  36. //Zend_Layout::startMvc($config->appearance);
  37. Zend_Layout::startMvc($options);
  38.  
  39. require_once 'Zend/Controller/Front.php';
  40. $frontController = Zend_Controller_Front::getInstance();
  41. $frontController->addModuleDirectory($rootPath . '/application/modules')
  42.                 ->throwExceptions(true)
  43.                 ->dispatch();

Observando, nos damos cuenta de que Zend_Controller_Front agrega la raíz del directorio modules el cual contendrá todos los módulos del sistema, mediante el método addModuleDirectory.

El método addModuleDirectory() internamente implementa SPL DirectoryIterator y agrega los nombres directorio (dentro de la carpeta "modules") como módulos o sub-sitios del sistema, lo que lo hace más eficiente y menos lineas de código por parte del cliente.

Ahora nos quedaría mover las vistas, modelos y controladores según la nueva estructura (Figura Nº1), recuerden que para este ejemplo los modelos los vamos a dejar en la carpeta \quickstart\application\models y separados por carpetas con el nombre del modulo correspondiente.

Luego hay que cambiar el nombre de las clases controladoras para agregar el prefijo namespacing, excepto en el módulo default.

Módulo User:

User_IndexController (IndexController.php)

PHP:
  1. <?php
  2. require_once 'Zend/Controller/Action.php';
  3.  
  4. require_once 'user/UserModel.php';
  5.  
  6. class User_IndexController extends Zend_Controller_Action
  7. {
  8. /* ... etc... */
  9. }

Módulo Default:

IndexController (IndexController.php, modulo default no lleva prefijo, se mantiene igual)

PHP:
  1. <?php
  2. require_once 'Zend/Controller/Action.php';
  3. class IndexController extends Zend_Controller_Action
  4. {/* ... etc... */}

Nota Importante: Todos los includes o require de los modelos hay que modificarlos , ya que ahora están dentro de la carpeta correspondiente a su modulo.

PHP:
  1. require_once 'user/UserModel.php';
  2.  
  3. require_once 'default/CommentsTable.php';

También hay que modificar las url en el Layout y Views.

Ejemplo:

PHP:
  1. <div>
  2.               <a href="<?php echo $this->baseUrl; ?>">Home</a>
  3.             | <a href="<?php echo $this->baseUrl; ?>/about">About</a>
  4.             <?php if($this->loggedIn):?>
  5.                 | <a href="<?php echo $this->baseUrl; ?>/user">Perfil</a>
  6.             <?php else:?>
  7.                 | <a href="<?php echo $this->baseUrl; ?>/user">Login</a>
  8.             <?php endif;?>
  9.            
  10.             <?php if($this->loggedIn):?>
  11.                 | <a href="<?php echo $this->baseUrl; ?>/user/index/logout">Logout (<?php echo $this->user->username;?>)</a>
  12.             <?php endif;?>     
  13.         </div>

Hemos llegado al final del articulo, hemos hecho un completo refactoring a nuestra aplicación agregando más potencial con la estructura modular de directorios la cual nos permitirá organizar de mejor forma nuestras aplicaciones MVC y hacer un más eficiente re uso de estos.

En próximos post veremos como centralizar toda la inicialización del bootstrap en una sola clase, permitiendo un index.php mucho más limpio y delegar todos los procesos a una sola clase que tendrá un método estático Main que es la puerta de entrada a nuestro sistema al puro estilo C#/JAVA.

Espero que sirva.

Comentarios

18 Responses to “Estructura Modular de Directorios”

  1. Gonzalo on Junio 8th, 2008 8:56 pm

    Hola! estoy siguiendo tu tutorial para hacer una aplicacion modular, pero tengo problemas con el modls donde tengo un formulario de contacto. este es mi index:

    los directorios le configure asi

    /** Configuración de Directorios */
    $rootPath = dirname(dirname(__FILE__));
    set_include_path(get_include_path() . PATH_SEPARATOR .
    $rootPath . ‘/aplicaciones/config’ . PATH_SEPARATOR .
    $rootPath . ‘/aplicaciones/modules/contacto/models’ . PATH_SEPARATOR .
    $rootPath . ‘/aplicaciones’ . PATH_SEPARATOR .
    $rootPath . ‘/libreria’ . PATH_SEPARATOR .
    $rootPath . ‘/libreria/incubator’ . PATH_SEPARATOR .
    $rootPath . ‘/public_html’);

    y este es el vciew helper del formulario

    /** Inicializa el incubator view helpers para Zend_Form*/
    $view = new Zend_View();
    $view->addHelperPath(’path/to/incubator/libreria/Zend/View/Helper/’);
    Zend_Controller_Action_HelperBroker::getStaticHelper(’viewRenderer’)
    ->setView($view);

    me podrias ayudar con esto?

    saludos!

  2. Gonzalo on Junio 8th, 2008 9:00 pm

    perdon por lo de los comentarios:S

  3. Oscar on Julio 10th, 2008 10:44 am

    Hola,
    muy buen tutorial, vengo del mundo de java y me esta siendo muy util para estructurar una aplicacion,
    Solo tengo un problema, hay alguna forma de tener un helper comun a todos los modulos o tengo que copiarmelo en cada modulo??

    Un saludo y gracias.

  4. zsamer on Julio 10th, 2008 1:09 pm

    Hola oscar, si se puede. tienes que crear un Namespace(directorio) en la carpeta library, puedes llamarlo como quieras luego ahí mantienes la misma estructura de Zend, es decir en el caso de agregar un helper view tendrías que dentro de tu namespace crear un sub directorio View y luego Helper.

    Ejemplo: library\Zsamer\View\Helper (Zsamer es mi Namespace donde guardo las clases que heredo de Zend Framework y además para agregar los helpers action y/o helpers view, plugins etc…).

    Bueno, en la carpeta Helper guardas todos los Helper comunes para todos los módulos.

    Finalmente y lo más importante tienes que decirle a Zend_View que tenga en cuenta el nuevo Namspace de Helpers:

    $this->view->addHelperPath(Core::getBaseDir() . DIRECTORY_SEPARATOR . ‘library/Zsamer/View/Helper’, ‘Zsamer_View_Helper_’);

    eso sería en todo caso en el manual de Zend Framework explica más detallado esto.

    saludos.

  5. Oscar on Julio 14th, 2008 8:10 am

    Muchas gracias Solucionado.
    ahora a seguir probando cositas…

  6. Zend_Acl y MVC Integración | Zend Framework: Estado del Arte on Julio 22nd, 2008 6:54 pm

    [...] En resumen trata sobre la integración de Zend Acl con MVC Estructura Modular. [...]

  7. jose on Octubre 6th, 2008 10:18 pm

    Szamer unas pequeñas dudas, si eres tan amable de aclararmelas.
    ¿Por que no usaste un bootstrap?
    ¿Usas el helper baseUrl, debo declarar la clase en cada carpeta views de los modules? o lo que es lo mismo ¿Donde colocaria el helper baseUrl para que lo usen todos los modulos?

  8. zsamer on Octubre 6th, 2008 11:33 pm

    no entiendo bien tus dudas.

    Si usé bootstrap, si un bootstrap o index no funciona ZF.

    baseUrl es una variable de la vista no un helper.

  9. jose on Octubre 7th, 2008 1:59 am

    disculpa por mi poca exactitud, pero vi en un post posterior que creaste un bootstrap.php para depurar el index.
    En cuanto a baseUrl pensaba que yo tenia que crear un archivo llamado BaseUrl.php dentro de views/helpers como lei aqui:
    http://es.wikibooks.org/wiki/Zend_Framework/Configuracion_Basica

  10. zsamer on Octubre 7th, 2008 2:33 am

    Esta bien, ambas opciones son muy validas. Es cosa de comodidad, crear un helper view para baseUrl es buena idea.

    Como en el post anterior también puedes crear un bootstrap.php.

    Saludos,

  11. jose on Octubre 7th, 2008 2:55 am

    pero lo que no entiendo es que para la modularizacion de un sistema tendria que crear un helper view en cada modulo para baseUrl? no me parece optimo.
    Podrias explicar un poco mas tu proposición: “baseUrl es una variable de la vista no un helper”.
    Gracias por tu colaboracion.
    jose

  12. zsamer on Octubre 7th, 2008 4:14 am

    Es tan simple como agregar lo siguiente en el metodo int de tu Controller:

    $this->view->baseUrl = this->_request->getBaseUrl();

    En caso de un helper view que sea común para todos los módulos: tienes que crear un Namespace(directorio) en la carpeta library, puedes llamarlo como quieras luego ahí mantienes la misma estructura de Zend, es decir en el caso de agregar un helper view tendrías que dentro de tu namespace crear un sub directorio View y luego Helper.

    Ejemplo: library\Zsamer\View\Helper (Zsamer es mi Namespace donde guardo las clases que heredo de Zend Framework y además para agregar los helpers action y/o helpers view, plugins etc…).

    Bueno, en la carpeta Helper guardas todos los Helper comunes para todos los módulos.

    Finalmente y lo más importante tienes que decirle a Zend_View que tenga en cuenta el nuevo Namspace de Helpers:

    $this->view->addHelperPath(Core::getBaseDir() . DIRECTORY_SEPARATOR . ‘library/Zsamer/View/Helper’, ‘Zsamer_View_Helper_’);

    eso sería en todo caso en el manual de Zend Framework explica más detallado esto.

  13. jose on Octubre 7th, 2008 4:21 am

    muchas gracias… entendido…
    jose

  14. Top Posts 2008 | Zend Framework: Estado del Arte on Enero 11th, 2009 4:25 am

    [...] Estructura Modular de Directorios [...]

  15. Josvil on Febrero 23rd, 2009 11:46 pm

    Hola, primero felicitaciones por el tutorial.

    He podido realizar todos los posts sin ningun problema, hasta este.

    Al colocar public, aparece en pantalla el msn que hace falta estar logeado para poder ingresar comentarios.

    Al pulsar sobre el enlace “login”, me sale el siguiente error:
    Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with message ‘Invalid controller specified (login)’

    Tuve que realizar las siguientes modificaciones para que funcionara:

    Archivo: index.phtml
    Dir: apps\modules\default\views\scripts\index
    cambie:
    baseUrl;?>/user/login” por
    baseUrl;?>/user/index/login”

    Archivo: index.phtml
    Dir: apps\models\user
    ‘action’ => $this->view->baseUrl . ‘/user/login’, por
    ‘action’ => $this->view->baseUrl . ‘/user/index/login’,

    En el index.php tengo:
    $frontController->addModuleDirectory($rootPath . ‘/apps/modules’)

    Queria saber que me falta colocar para que funcione tal cual como lo tienes en el post?

    Saludos y gracias de antemano

  16. gus on Abril 17th, 2009 10:00 pm

    zsamer: Ante todo muchas gracias por el sitio y los posts estan muy buenos.Estoy empezando con ZF implemente estos ejemplos el login,y la modularizacion pero no entiendo porque no me da el siguiente error

    Warning: require_once(user/models/UserModel.php) [function.require-once]: failed to open stream: No such file or directory in C:\htdocs\sysacc\application\modules\default\controllers\IndexController.php on line 45

    y en la linea 45 tengo segun reviso la estructura apuntado bien el include

    public function preDispatch()
    {

    require_once ‘user/models/UserModel.php’;

    si alguien puede darme una indicacion como debuggear esto agradezco desde ya

  17. Gary Prado on Febrero 1st, 2010 10:17 pm

    Hola, e seguido tus anteriores tutoriales y son exelentes te felicito, justamente necesito ayuda ya q estoy haciendo un proyecto pero he movido los archivos del model a la carpeta correspondiente al modulo, pero me sale el sgte error, es como si no conociera los modelos, en su modulo:

    Warning: IndexController::require_once(default/CommentsTable.php) [function.IndexController-require-once]: failed to open stream: No such file or directory in C:\AppServ\www\Pruebas\demoZend\application\modules\default\controllers\IndexController.php on line 23

    Fatal error: IndexController::require_once() [function.require]: Failed opening required ‘default/CommentsTable.php’ (include_path=’.;C:\php5\pear;C:\AppServ\www\Pruebas\demoZend/application/config;C:\AppServ\www\Pruebas\demoZend/application/models;C:\AppServ\www\Pruebas\demoZend/library;C:\AppServ\www\Pruebas\demoZend/public’) in C:\AppServ\www\Pruebas\demoZend\application\modules\default\controllers\IndexController.php on line 23

    disculpa la molestia… GRACIAS DE ANTEMANO.

  18. Gary Prado on Febrero 1st, 2010 10:19 pm

    He intentado poner require_once(default/models/CommentsTable.php), pero igual no me da, sera q tengo q configurar algo en el bootstrap ??
    GRACIAS

Deja tu comentario




XHTML: puedes usar estas etiquetas: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>