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

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):
-
class User_IndexController extends Zend_Controller_Action {
-
public function indexAction() {}
-
}
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:
-
:module/:controller/:action/
-
http://example.com/user/index/index
-
http://example.com/user/index/
-
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
-
/** Bootstrap */
-
/** Configuración de manejo de error */
-
date_default_timezone_set("America/Santiago");
-
-
/** Configuración de Directorios */
-
$rootPath . '/application/config' . PATH_SEPARATOR .
-
$rootPath . '/application/models' . PATH_SEPARATOR .
-
$rootPath . '/library' . PATH_SEPARATOR .
-
$rootPath . '/public');
-
-
/** Nuevo Autocarga de clases de Zend FrameWork */
-
require_once 'Zend/Loader.php';
-
Zend_Loader::registerAutoload();
-
-
/** Carga el archivo de configuración ini */
-
require_once 'Zend/Config/Ini.php';
-
$config = new Zend_Config_Ini('quickstart.ini', 'default');
-
-
/** Inicializa y conecta Base de Datos */
-
require_once 'Zend/Db.php';
-
require_once 'Zend/Db/Table/Abstract.php';
-
$db = Zend_Db::factory($config->database);
-
Zend_Db_Table_Abstract::setDefaultAdapter($db);
-
-
/** Inicializa layout */
-
'layout' => 'Main',
-
'layoutPath' => '../application/layouts/scripts/'
-
);
-
-
require_once 'Zend/Layout.php';
-
//Zend_Layout::startMvc($config->appearance);
-
Zend_Layout::startMvc($options);
-
-
require_once 'Zend/Controller/Front.php';
-
$frontController = Zend_Controller_Front::getInstance();
-
$frontController->addModuleDirectory($rootPath . '/application/modules')
-
->throwExceptions(true)
-
->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
-
require_once 'Zend/Controller/Action.php';
-
-
require_once 'user/UserModel.php';
-
-
class User_IndexController extends Zend_Controller_Action
-
{
-
/* ... etc... */
-
}
Módulo Default:
IndexController (IndexController.php, modulo default no lleva prefijo, se mantiene igual)
-
<?php
-
require_once 'Zend/Controller/Action.php';
-
class IndexController extends Zend_Controller_Action
-
{/* ... 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.
-
require_once 'user/UserModel.php';
-
-
require_once 'default/CommentsTable.php';
También hay que modificar las url en el Layout y Views.
Ejemplo:
-
<div>
-
<a href="<?php echo $this->baseUrl; ?>">Home</a>
-
| <a href="<?php echo $this->baseUrl; ?>/about">About</a>
-
<?php if($this->loggedIn):?>
-
| <a href="<?php echo $this->baseUrl; ?>/user">Perfil</a>
-
<?php else:?>
-
| <a href="<?php echo $this->baseUrl; ?>/user">Login</a>
-
<?php endif;?>
-
-
<?php if($this->loggedIn):?>
-
| <a href="<?php echo $this->baseUrl; ?>/user/index/logout">Logout (<?php echo $this->user->username;?>)</a>
-
<?php endif;?>
-
</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”
Deja tu comentario

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!
perdon por lo de los comentarios:S
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.
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.
Muchas gracias Solucionado.
ahora a seguir probando cositas…
[...] En resumen trata sobre la integración de Zend Acl con MVC Estructura Modular. [...]
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?
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.
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
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,
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
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.
muchas gracias… entendido…
jose
[...] Estructura Modular de Directorios [...]
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
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
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.
He intentado poner require_once(default/models/CommentsTable.php), pero igual no me da, sera q tengo q configurar algo en el bootstrap ??
GRACIAS