Tutorial manos a la obra con Yupp PHP Framework

1,189 views
1,113 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,189
On SlideShare
0
From Embeds
0
Number of Embeds
40
Actions
Shares
0
Downloads
34
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Tutorial manos a la obra con Yupp PHP Framework

  1. 1. Yupp PHP Framework Tutorial “manos a la obra” Autor: Ing. Pablo Pazos Gutiérrez <pablo.swp@gmail.com> Fecha: Mayo 2011 Versión: 1.1
  2. 2. Índice1. Fundamentos de patrones MVC y ORM...........................................................................................4 1.1 Model-View-Controller (MVC).....................................................................................................4 1.1.1 Algunos conceptos a importantes de recordar:....................................................................5 1.2 Object-Relational Mapping (ORM)..............................................................................................6 1.2.1 Mapeo objeto-relacional.......................................................................................................6 1.2.1.1 Mapeo de clase..............................................................................................................6 1.2.1.2 Mapeo de relaciones......................................................................................................7 1.2.1.3 Mapeos de herencia......................................................................................................82. Fundamentos de programación ágil y aplicaciones web.................................................................103. Instalación y configuración del framework......................................................................................12 3.1 Descargar la liberación.............................................................................................................12 3.2 Descargar del servidor de desarrollo........................................................................................13 3.3 Configuración del framework....................................................................................................144. Estructura de Yupp Framework......................................................................................................155. URLs en Yupp................................................................................................................................186. Creando una aplicación simple.......................................................................................................197. Estructura de las aplicaciones Yupp...............................................................................................24 7.1 Configuración de la base de datos por aplicación.....................................................................25 7.1.1 Creando la configuración....................................................................................................25 7.2 Script de bootstrap....................................................................................................................26 7.3 Scripts de testing......................................................................................................................268. Model: funcionalidades avanzadas.................................................................................................28 8.1 Gestionando relaciones hasOne...............................................................................................28 8.2 Gestionando relaciones hasMany.............................................................................................30 8.2.1 Tipos de relaciones hasMany.............................................................................................31 8.3 Convenciones sobre nombrado de tablas.................................................................................32 8.3.1 Nombrado implícito de tablas.............................................................................................32 8.3.2 Nombrado explícito de tablas.............................................................................................32 8.3.3 Nombrado de tablas de join................................................................................................33 8.4 Creando restricciones sobre campos y relaciones....................................................................33 8.4.1 Definiendo restricciones.....................................................................................................33 8.4.2 Verificando restricciones....................................................................................................35 8.4.2.1 Validando datos mediante restricciones.......................................................................35 8.4.2.2 Validando datos previa a la persistencia......................................................................35 8.5 Definiendo lógica pre-validación...............................................................................................36 8.6 Usando belongsTo....................................................................................................................37 8.7 Definiendo relaciones de muchos a muchos.............................................................................40 8.8 Eliminación física vs. eliminación lógica....................................................................................409. Controladores.................................................................................................................................42 9.1 Convenciones...........................................................................................................................42 9.2 Acciones de infraestructura (scaffolded)...................................................................................44 9.3 Recibiendo archivos..................................................................................................................44 9.4 Devolviendo XML o JSON........................................................................................................46 9.4.1 Programando una acción que devuelve JSON...................................................................46 9.4.2 Programando una acción que devuelve XML.....................................................................4710. Vistas...........................................................................................................................................48 10.1 Fundamentos para la implementación de vistas.....................................................................48
  3. 3. 10.2 Uso de helpers........................................................................................................................49 10.2.1 Helper layout....................................................................................................................49 10.2.2 Helper template................................................................................................................50 10.2.3 Helper javascript...............................................................................................................52 10.2.4 Helper ajax link.................................................................................................................52 10.3 Vistas de infraestructura (scaffolded)......................................................................................5311. Estado actual del proyecto...........................................................................................................54 11.1 Hoja de ruta del proyecto........................................................................................................54 11.2 Información administrativa......................................................................................................54
  4. 4. 1. Fundamentos de patrones MVC y ORM1.1 Model-View-Controller (MVC)MVC es un patrón arquitectónico, que determina grandes componentes de software, a modo decapas, que se dividen las responsabilidades funcionales de un sistema informático. Es frecuente laasociación de MVC al modelo de 3 capas: interfaz de usuario, lógica de negocio y acceso a datos,donde “view” se asocia a la interfaz de usuario, “controller” a la lógica de negocio, y “model” alacceso a datos.Existen múltiples implementaciones de MVC, en esta sección veremos la alternativa deimplementación elegida para Yupp Framework. En el siguiente diagrama se muestra un ciclo depedido, donde se pasa por los 3 componentes de MVC.En el diagrama se muestra un modelo simplificado del MVC de Yupp Framewrok (YMVC), donde unusuario que realiza un pedido desde su navegador web, y este es atendido por el componenteController. En Controller se ejecuta la lógica de negocio, se accede a los datos mediante elcomponente Model, donde se hacen consultas y actualizaciones. Luego el componente View es elque determina que respuesta se le dará al usuario. View también puede acceder a Model pero engeneral no accede directamente, sino que usa el resultado de la ejecución de la lógica, provisto porController. Luego Controller entrega la respuesta al usuario, en general es una página web con datosde las entidades del Model.
  5. 5. 1.1.1 Algunos conceptos a importantes de recordar: ● Un controlador implementa cierta lógica que es ejecutada dependiendo del pedido recibido. ● La lógica se implementa en forma de acciones, cada acción es un método dentro del controlador. Un controlador puede tener varias acciones. ● Cada acción determina que vista se va a mostrar al usuario, lo que depende de los parámetros de entrada y la lógica que se ejecute. ● Desde una acción, se pueden mostrar varias vistas distintas, solo una a la vez. ● Cada acción determina que modelo pasarle a cada vista.A continuación se muestra un diagrama más detallado que se aproxima mejor a la implementaciónde YMVC.1.1.2 Descripción de los componentes:Routing: se encarga de recibir un pedido del usuario y determinar qué controller lo debe atender.Una instancia de Yupp puede contener múltiples aplicaciones, cada una con múltiples controllers.Filter: es un componente que sirve para realizar acciones previas a la ejecución de la lógica delcontroller seleccionado. Es útil para realizar verificaciones de seguridad, por ejemplo verificar si elusuario tiene permisos para ejecutar la lógica del controller o no.Data Access Layer (DAL): componente que abstrae el acceso a distintos DBMS, de modo que elframework pueda trabajar, con MySQL o Postgres de forma transparente para el usuario.
  6. 6. La clase RequestManager es la que se encarga de recibir los pedidos, procesar los parámetros,utilizar routing para determinar el controller y acción a ejecutar, procesar el resultado, y devolver unavista. También se encarga de procesar errores y mostrarlos de forma amigable al usuario.1.2 Object-Relational Mapping (ORM)ORM es un mecanismo que permite trabajar con objetos en lugar de registros, en el acceso a basesde datos relacionales. Esto es de especial interés para quienes programamos usando Orientación aObjetos, porque evita la complejidad de tener que trabajar con estructuras de registros relacionales.Para ORM también existen múltiples alternativas de implementación, en esta sección nosconcentraremos en la implementación de ORM elegida para Yupp (YORM).Objetivos principales de YORM: ● Definir modelos de datos complejos, completamente orientados a objetos ● Evitar al máximo trabajar con SQL ● Soportar múltiples gestores de bases de datosEl primer objetivo implica que se deben soportar el mapeo de clases a tablas, el mapeo derelaciones entre clases, y el mapeo de herencia entre clases. El segundo objetivo implica que seutilizarán estructuras de clases para crear las consultas a las bases de datos, en lugar de utilizarSQL. Y el tercer objetivo implica que el framework se debe abstraer del gestor de bases datos. Comovimos en el MVC, esto se hace con el componente DAL.1.2.1 Mapeo objeto-relacional1.2.1.1 Mapeo de claseEl primer problema a resolver es el de mapear una clase simple a una tabla en la base de datosrelacional. Esto es relativamente sencillo de hacer en general, pero con PHP existe un problemaagregado: PHP es dinámico y débilmente tipado, y las bases de datos utilizan registros fuértementetipados, por lo que para cada campo de una clase programada en PHP, es necesario contar con unmecanismo de especificación del tipo, ya que PHP no lo tiene.El siguiente diagrama muestra la correspondencia entre la definición de una clase y sucorrespondiente mapeo a una tabla.
  7. 7. La implementación de esta clase en Yupp es similar a este código:// Todas clas clases persistentes heredan de PersistentObjectclass Usuario extends PersistentObject{ // Los campos se definen en el constructor function __construct($args = array (), $isSimpleInstance = false) { // Determina el nombre de la tabla $this->setWithTable("usuarios"); // Campos de la clase $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("email", Datatypes :: TEXT); $this->addAttribute("edad", Datatypes :: INT_NUMBER); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); // Llamada al constructor de la superclase // Inyecta atributos útiles para ORM (id, class, deleted) parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}1.2.1.2 Mapeo de relacionesHabiendo definido 2 clases, estas pueden relacionarse de múltiples formas. Las relaciones entreclases pueden ser unidireccionales o bidireccionales, y pueden tener distintas cardinalidades. Paralas cardinalidades, diferenciaremos 2 casos: 1 y N. A continuación se muestra un diagrama contodas las posibilidades de definición de relaciones entre 2 clases:Para las relaciones con un lado N, aparte de las tablas que el YORM creará para las clases A y B,creará una tabla intermedia (tabla de join) para mantener las relaciones. A continuación se muestraun ejemplo. Si a es una instancia de A y b1, b2, b3 son instancias de B, y A tiene varios B (en estecaso b1, b2 y b3), las relaciones se mantienen de la siguiente forma:
  8. 8. tabla_a tabla_a_b tabla_bid id_a id_a id_b1 id id_b1 id_a id_b2 id id_b2 id_a id_b3 id id_b3A continuación se muestra el código PHP de la clase A con una relación unidireccional a un B:class A extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->setWithTable("tabla_a"); // Declaración de campos de la clase A omitidos .. $this->addHasOne("b", "B"); // Declaración de relación a un B // Llamada al constructor de la superclase // Inyecta atributos útiles para ORM (id, class, deleted) parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}En el caso de que la clase A tuviera relacionados varios B, la definición de la clase sería:class A extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->setWithTable("tabla_a"); // Declaración de campos de la clase A omitidos .. $this->addHasMany("b", "B"); // Declaración de relación a muchos B // Llamada al constructor de la superclase // Inyecta atributos útiles para ORM (id, class, deleted) parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}1.2.1.3 Mapeos de herencia
  9. 9. Las clases persistentes soportan la definición de herencia (especialización) como en cualquiermodelo orientado a objetos. Al conjunto de una clase y todas las clases que heredan de esta, directao indirectamente, se le llama “estructura de herencia” o “árbol de herencia”.Por ejemplo, si se tienen 2 clases C y D, donde D hereda de C, hay dos opciones de ORM: ● mapear las dos clases a la misma tabla, ó ● que cada clase tenga su propia tabla.Caso 1: D hereda de C y se mapean en la misma tabla.Caso 2: D hereda de C y se mapean en tablas distintas:En el caso 2 se puede apreciar que en la tabla_d se inyecta un nuevo atributo “super_id_c”, queindica cual es el identificador de la parte de la clase D que se hereda de C, y sirve para hacer un joinentre las tablas, de modo de obtener una instancia completa de D, que incluye los valores de losatributos declarados tanto en la clase C como en la clase D.También para el caso 2 se debe notar que el identificador de las instancias de D será en que seguarda en la tabla “tabla_d”, ya que es la tabla que corresponde con la clase concreta. Esteidentificador puede ser distinto al que se guarda en la tabla “tabla_c” (por eso es necesaria lacolumna “super_id_c”). Para una instancia de D persistita, en la columna “class” de ambas tablasestará el valor “D”. Las instancias de C persistidas, no tendrán ningún registro ingresado en la tabla“tabla_d”.
  10. 10. 2. Fundamentos de programación ágil yaplicaciones webEn general, el desarrollo ágil refiere a una característica de algunos procesos de desarrollo desoftware, que permiten que un proyecto tenga: ● Productos tangibles y funcionales en períodos de tiempo cortos ● Mayor visibilidad del avance del proyecto para el cliente ● Capacidad de adaptación rápida a cambios en los requerimientosQue esto sea posible, también depende de las tecnologías que se empleen para los proyectos. Enparticular los “frameworks ágiles”, como lo es Yupp, han propuesto tecnologías para acompañar yayudar a los procesos de desarrollo ágil de software. En este sentido, los objetivos de Yupp son: ● Quitar de manos del programador las tareas repetitivas que agregan poco valor al proyecto (consultas a la BD, validación de datos, internacionalización, ajax, etc) ● Seguir el paradigma “convención sobre configuración”, que marca reglas básicas o convenciones, que hacen que no se necesiten grandes y dificultosas configuraciones por cada proyecto. ● Fácil de instalar y usar. ● Minimalista, todo lo necesario, pero no más. ● Permitir generar aplicaciones web funcionales sin necesidad de realizar una programación completa. ● Curva de aprendizaje reducida.Aplicaciones webPrincipales características de las aplicaciones web: ● Instaladas en la nube ● No necesitan actualizaciones ● Accedidas desde web browsers ● Ejecución mixta, parte en el cliente, parte en el servidor (puede ser más de uno)Las tendencias actuales muestran que en el futuro las aplicaciones basadas en web serán las únicasaplicaciones, y que las aplicaciones de escritorio poco a poco van a desaparecer. Además existe unatendencia de empresas como Google y Mozilla al desarrollo de sistemas operativos y aplicacionestodo basado en web.Por otro lado, con el desarrollo del nuevo estándar HTML5, que incluye de forma nativa soporte paravideo, sonido, multi-threading, primitivas gráficas 2D y 3D, y otras características, lo que da unainfraestructura enorme para el desarrollo de aplicaciones web. En este sentido, Yupp está intentando
  11. 11. posicionarse como una opción competitiva entre los frameworks ágiles para el desarrollo deaplicaciones para esta nueva realidad que está comenzando.Algunos vínculos interesantes: ● http://www.youtube.com/watch?v=42Gyy2xr5zA ● http://www.youtube.com/watch?v=ErqCqwkwIDE ● http://www.youtube.com/watch?v=jB5KFJULahs ● http://www.youtube.com/watch?v=KMZLM2AhSE0 ● http://www.youtube.com/watch?v=ANMrzw7JFzA
  12. 12. 3. Instalación y configuración delframeworkLos siguientes pasos suponen que se tiene un servidor web con soporte para PHP instalado. Enparticular, se referenciará a la aplicación WAMP que es un paquete que trae Apache, MySQL y PHPpara Windows. Por más información: http://www.wampserver.comEl primer paso es obtener la última versión del framework, para esto hay dos opciones: ● Descargar la liberación ● Descargar del repositorio de desarrollo3.1 Descargar la liberaciónDesde la página de Google Code del proyecto [1], en el área de descargas, residen las últimasliberaciones en archivos en formato ZIP.[1] http://code.google.com/p/yupp/
  13. 13. 3.2 Descargar del servidor de desarrolloEn Google Code también se encuentra el servidor de versiones (SVN) del proyecto. El código fuentede desarrollo se puede descargar desde ahí [2] con cualquier cliente SVN.[2] http://yupp.googlecode.com/svn/YuppPHPFrameworkUna vez descargado el framework, se copia su contenido al directorio “www” del WAMP. Paraverificar que se ha copiado correctamente, se inicia el WAMP, en un navegador web se accede ahttp://localhost, donde deberá aparecer una página del WAMP. En esa página, hay una zona deproyectos, ahí debería aparecer el directorio descomprimido donde se ha copiado el framewok. Si seingresa al directorio del framework, se debería ver el escritorio de Yupp como se muestra en lasiguiente imagen:En este escritorio aparecerán todas las aplicaciones instaladas en Yupp Framework. Podemoscomentar varios aspectos de Yupp: ● Soporta múltiples aplicaciones. ● Sirve para que el desarrollador tenga varios proyectos en los que está trabajando. ● Sirve para que el usuario final pueda usar varias aplicaciones.Yupp es tanto un plataforma de desarrollo, como plataforma de ejecución de aplicaciones web.Otros aspectos del escritorio son: ● Desde aquí se pueden crear nuevas aplicaciones ● Se pueden filtrar las aplicaciones (útil cuando se tiene una cantidad considerable) ● Se pueden ver novedades relacionadas con Yupp y publicadas en Twitter ● Se puede acceder al listado de aplicaciones (vista por defecto) ● Se puede acceder a la vista de Base de Datos (es necesaria la configuración previa) ● Se pueden ejecutar los tests de las aplicaciones ● Se pueden ejecutar los scripts de bootstrap de las aplicaciones ● Se pueden ejecutar las aplicacionesNota: si se intenta acceder a la vista de Bases de Datos se obtendrá un error que indicará que labase de datos no existe. Esto se debe a que todavía no la hemos creado y configurado.
  14. 14. 3.3 Configuración del frameworkYupp Framework fue diseñado para que la configuración sea mínima, representando una tarea muysimple a realizar una sola vez. La única configuración necesaria es la de la base de datos a utilizar.Al día de hoy, Yupp Framework soporta MySQL, Postgres y SQLite. Para configurar el gestor debase de datos, es necesario editar el archivo yupp/core/config/core.config.YuppConfig.class.php. Eneste archivo hay un campo $default_datasource, donde se realiza esta configuración en unaestructura como esta:$default_datasource = array( self::MODE_DEV => array( type => self::DB_MYSQL, url => localhost, user => root, pass => , database => yupp_dev ), self::MODE_PROD => array( type => self::DB_MYSQL, url => localhost, user => root, pass => , database => yupp_prod ), self::MODE_TEST => array( type => self::DB_MYSQL, url => localhost, user => root, pass => , database => yupp_test ));En esta estructura se indica que configuración de base de datos usar para cada modo de ejecución.Hoy el framework soporta los modos “desarrollo” y “producción”, en un futuro también soportará elmodo “testing”. Para cada modo de ejecución se indica: ● tipo de gestor de bases de datos (MySQL, Postgres o SQLite) ● ubicación del gestor de bases de datos ● usuario y clave ● nombre de la base de datos a utilizar
  15. 15. 4. Estructura de Yupp FrameworkLa estructura (reducida) del framework es la siguiente: ● apps ○ core ○ tests ■ app.xml ● core ○ app ○ basic ○ config ○ db ○ http ○ layout ○ mvc ○ persistent ○ routing ○ support ○ testing ○ utils ○ validation ○ web ○ App ○ FileSystem ○ Yupp ○ YuppLoader ○ YuppSession ● css ● images ● js ● .htaccess ● index.phpExplicación de la estructura/apps: Este directorio contiene las aplicaciones instaladas en el framework/apps/core: Esta aplicación es parte del framework. Todas las vistas y acciones como el escritoriode Yupp, la vista de Bases de Datos, la creación de aplicaciones, etc, son parte de esta aplicación./apps/tests: Esta aplicación contiene algunos tests sobre el framework. Por un lado, los testsautomatizados que pueden ser ejecutados desde el escritorio de Yupp. Por otro lado, al ejecutar laaplicación, se tienen algunas vistas con tests para ejecutar manualmente, que además sirven comoreferencia para la programación.
  16. 16. /apps/tests/app.xml: Descriptor de aplicaciones, usado por Yupp para presentar correctamente laaplicación en el escritorio. En el futuro servirá para gestionar aplicaciones./core: Es el núcleo del framework. Contiene todas las clases y scripts necesarios para elfuncionamiento del framework./core/app: Contiene recursos relacionados con el manejo de las aplicaciones./core/basic: Contiene clases útiles para manejar tipos básicos como String y DateTime./core/config: Contiene elementos de configuración y la implementación de muchas de lasconvenciones de Yupp, como las de nombrado de los archivos./core/db: Contiene la implementación de la capa de acceso a datos, los conectores a los distintosmotores de bases de datos, y el paquete de creación de consultas./core/http: Contiene la implementación de HTTPRequest y HTTPResponse./core/layout: Contiene la clase que da soporte a la definición de layouts en las vistas./core/mvc: Contiene la implementación de los elementos básicos para soportar controladores,vistas, helpers útiles para la generación de vistas, y para el pasaje del modelo desde el controlador ala vista./core/persistent: Contiene la implementación del ORM de Yupp. y clases para la serialización aJSON y XML./core/routing: Este directorio contiene las clases que implementan el ruteo de pedidos al framework,y la ejecución de acciones de los controladores./core/support: Contiene algunas clases útiles para mantener el contexto de ejecución delframework, para el manejo de medidas de tiempo y para soportar la internacionalización./core/testing: Contiene las clases necesarias para implementar tests automáticos./core/utils: Contiene clases utilitarias de uso interno del framework./core/validation: Contiene las clases que implementan la validación de campos para las clasespersistentes./core/web: Contiene las clases que se encargan de manejar los pedidos al framework, procesar urls,parámetros, y coordinar toda la ejecución de acciones y devolución de vistas.App: Clase que implementa operaciones útiles sobre las aplicaciones.FileSystem: Clase que implementa métodos útiles para acceder al sistema de archivos.Yupp: Clases que implementa operaciones útiles sobre el framework.YuppLoader: Clase que implementa la carga controlada de clases y scripts.
  17. 17. YuppSession: Clase que implementa la gestión de la sesión.css: En este directorio se colocan las hojas de estilo de uso global (útiles para varias aplicaciones).images: En este directorio se ponen las imágenes de uso global.js: En este directorio se ponen los archivos javascript para uso global..htaccess: Indica reglas de redirección de pedidos, todos los pedidos van al index.php.index.php: Punto de entrada de los pedidos del usuario al framework.
  18. 18. 5. URLs en YuppComo se mencionó antes, Yupp es un framework orientado a convenciones. La primer convenciónque debe quedar clara es la del formato de las URLs que el framework puede recibir. Esto es muyimportante, porque toda la ejecución del framework y de sus aplicaciones dependerá de las URLsque se reciban.Antes de entrar en los formatos de las URLs, algunos comentarios: ● Yupp soporta varias aplicaciones ● Cada aplicación puede tener varios controladores ● Cada controlador implementa varias acciones (métodos) ● Dependiendo de los parámetros de entrada, una misma acción puede indicar que se muestren varias vistas, una a la vez.Ejemplo de URL válida en Yupp: ● http://localhost/yupp/app/controller/action?param1=value1&param2=value2Si el framework fue copiado al directorio “yupp” dentro del directorio “www” del WAMP, esta deberíaser una URL válida para Yupp. A continuación se explica cada trozo de la misma: ● http://localhost: acceso al “www” del WAMP, también conocido como “http root”. ● yupp: directorio donde fue copiado el framework dentro del “www” del WAMP. ● app: nombre de la aplicación que se desea ejecutar. ● controller: nombre del controlador, dentro de la aplicación “app”, que se desea ejecutar. ● action: nombre de la acción a ejecutar. Esta acción está implementada en “controller”. ● ?param1=value1&param2=value2: parámetros que recibirá la acción.Por ejemplo, si se tiene una aplicación “blog” y se quiere acceder al listado de entradas, la siguientepodría ser la URL para hacerlo: ● http://localhost/yupp/blog/entradas/listYupp Framework soporta otra forma de pasarle parámetros a la acción, sin necesidad de usar elformato param=value, este es un ejemplo: ● http://localhost/yupp/app/controller/action/value1/value2Con este formato, el framework transformará los dos últimos trozos de la URL a parámetros denombre “_param_1” y “_param_2” respectivamente, y quedarán accesibles con esos nombres parala acción correspondient.
  19. 19. 6. Creando una aplicación simplePara crear una aplicación simple, se deben seguir estos pasos: 1. Ir al escritorio de Yupp Framework 2. Hacer clic sobre el link “Nueva aplicación” 3. Ingresar los datos ahí pedidos: a. Nombre de la aplicación: ingresar el nombre, por ejemplo “biblioteca” b. Descripción: una descripción de la aplicación, por ejemplo “Aplicación para gestión de libros” c. Lenguages: para qué idiomas estará disponible la aplicación, por ejemplo “es en” para español e inglés respectivamente. d. Nombre del controlador principal: nombre del controlador que se creará por defecto, por ejemplo “libro”. 4. Hacer clic en “crear”.Si se cumplieron todos los pasos exitosamente, ser debería volver al escritorio y ver la nuevaaplicación creada. Al hacer clic sobre el icono de la aplicación “biblioteca”, debería mostrarse el texto“Bienvenido a su nueva aplicación!”. En la siguiente sección se explica en detalle la estructurainterna de la aplicación creada de forma automática por el framework. Ingresando a “/yupp/apps”desde el sistemas de archivos, deberá haber un nuevo directorio con e nombre “biblioteca”. Dentrode éste, en el directorio “controllers” debería haber un archivo PHP que implementa el controlador“libro”, el archivo tendrá el nombre “apps.biblioteca.LibroController.class.php”. Abriendo ese archivoveremos que la clase tiene una acción implementada, la cual devuelve el mensaje que veíamospreviamente (Bienvenido a su nueva aplicación!).Creando una clase del modeloSupongamos que nuestra aplicación debe gestionar libros, entonces necesitamos una clase quemodele un libro. Un libro tiene un título, un género, autor, fecha de edición, idioma y número depáginas. Para crear esta clase, se deberían seguir los siguientes pasos: 1. Ir al directorio /yupp/apps/biblioteca/model 2. Ahí crear el archivo biblioteca.model.Libro.class.php 3. Programar una clase que herede de PersistentObject 4. Agregar un constructor al que se le pasan 2 parámetros: a. $args = array () b. $isSimpleInstance = false 5. Agregar llamada al constructor de la clase padre en el constructor de esta clase 6. Copiar los métodos estáticos de alguna clase de ejemplo 7. Agregar los atributos mencionados previamente: a. $this->addAttribute("titulo", Datatypes :: TEXT); b. $this->addAttribute("genero", Datatypes :: TEXT); c. $this->addAttribute("autor", Datatypes :: TEXT); d. $this->addAttribute("fecha", Datatypes :: DATETIME); e. $this->addAttribute("idioma", Datatypes :: TEXT); f. $this->addAttribute("numeroPaginas", Datatypes :: INT_NUMBER);
  20. 20. Vamos paso por pasoLuego de los primeros 5 pasos, deberíamos tener programada una clase como esta:class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { parent :: __construct($args, $isSimpleInstance); }}Todas las clases del modelo deben heredar de PersistentObject para contar con las funcionalidadesde persistencia. El constructor debe recibir estos dos parámetros para que internamente la clasePersistentObject realice las tareas necesarias para crear una clase persistente. Esas tareas sonejecutadas al hacer una llamada explícita al constructor de la clase padre, que en este caso es laúnica línea de código dentro del constructor de Libro.En el paso 6 se indica que deben copiarse los métodos estáticos, estos métodos permiten realizardistintas operaciones de persistencia y consulta sobre la clase. Luego del paso 6, la clase deberáquedar así:class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { parent :: __construct($args, $isSimpleInstance); } public static function listAll(ArrayObject $params) { self :: $thisClass = __CLASS__; return PersistentObject :: listAll($params); } public static function count() { self :: $thisClass = __CLASS__; return PersistentObject :: count(); } public static function get($id) { self :: $thisClass = __CLASS__; return PersistentObject :: get($id); } public static function findBy(Condition $condition, ArrayObject $params) { self :: $thisClass = __CLASS__; return PersistentObject :: findBy($condition, $params); } public static function countBy(Condition $condition) { self :: $thisClass = __CLASS__; return PersistentObject :: countBy($condition); }}
  21. 21. Por último, en el paso 7 agregamos los campos de la clase, cada uno con su respectivo tipo. Esnecesario definir el tipo del campo de forma explícita porque PHP no permite definir tipos para loscampos o atributos de una clase, porque PHP es debilmente tipado. Pero para indicarle a la base dedatos de qué tipo serán las columnas de la tabla donde se persistan instancias de la clase Libro, esnecesario la definición de los tipos.Luego de realizado el paso 7, la clase completa quedaría así:class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("titulo", Datatypes :: TEXT); $this->addAttribute("genero", Datatypes :: TEXT); $this->addAttribute("autor", Datatypes :: TEXT); $this->addAttribute("fecha", Datatypes :: DATETIME); $this->addAttribute("idioma", Datatypes :: TEXT); $this->addAttribute("numeroPaginas", Datatypes :: INT_NUMBER); parent :: __construct($args, $isSimpleInstance); } public static function listAll(ArrayObject $params) { self :: $thisClass = __CLASS__; return PersistentObject :: listAll($params); } public static function count() { self :: $thisClass = __CLASS__; return PersistentObject :: count(); } public static function get($id) { self :: $thisClass = __CLASS__; return PersistentObject :: get($id); } public static function findBy(Condition $condition, ArrayObject $params) { self :: $thisClass = __CLASS__; return PersistentObject :: findBy($condition, $params); } public static function countBy(Condition $condition) { self :: $thisClass = __CLASS__; return PersistentObject :: countBy($condition); }}
  22. 22. Para que nuestro controlador sepa de la existencia de la nueva clase del modelo y la pueda utilizar,debemos incluirla usando YuppLoader, así LibroController tendrá este aspecto:YuppLoader::load(biblioteca.model, Libro);class LibroController extends YuppController { public function indexAction() { return $this->renderString("Bienvenido a su nueva aplicacion!"); }}Suponiendo que ya tenemos la base de datos configurada y corriendo, procedamos a crear laestructura de la base para poder gestionar los libros. 1. Ingresar al escritorio de Yupp desde un navegador web 2. Ingresar a las pestaña “Base de Datos” 3. Debemos ver una zona para la aplicación “biblioteca”, donde se muestra que la clase Libro se almacena en cierta tabla, la cual aún no fue creada. 4. Hacer clic en el link “Crear tablas” 5. Si todo salió bien, en la zona de la aplicación “biblioteca” debería decir que la tabla para la clase Libro fue creada. 6. Volvemos al listado de aplicaciones (escritorio de Yupp) y hacemos clic sobre el icono de la aplicación “biblioteca”. 7. Si vemos el mensaje “Bienvenido a su nueva aplicacion!”, debemos estar en la siguiente URL: http://localhost/yupp/biblioteca/libro/index 8. Cambiar la url a: http://localhost/yupp/biblioteca/libro/list 9. El framework nos debería mostrar una vista con el listado de libros, sin ningún libro. Podemos apreciar una tabla que muestra todos los atributos de nuestra clase Libro. 10. Si hacemos clic en el link “Create”, nos debería mostrar una vista donde podemos ingresar toda la información de un Libro. 11. Cuidado con la fecha, ingresarla con el siguiente formato: aaaa-mm-dd 12. Al hacer clic en el botón “Create”, debemos estar en una vista que muestra los datos ingresado. 13. Volvemos al listado cliqueando en el link “list”, y nos debería mostrar el libro recién ingresado. 14. Podemos seguir agregando libros, editando sus datos, o eliminándolos.Algunos comentarios respecto a los pasos previos:Generación de estructuras en la base de datos:Toda la generación de tablas y relaciones en la base de datos la realiza el framework de formaautomática, en base a las clases definidas en el modelo de cada aplicación.Vistas dinámicas:Todas las vistas que se ven en el flujo de uso de la aplicación (listado, detalles del libro, creación,edición) son autogenerdas, no fueron programadas como parte de la aplicación.Acciones dinámicas:
  23. 23. Todas las acciones de listado, altas, bajas y modificaciones están implementadas en el framework, elprogramador nunca implementó estas operaciones. Es más, en el controlador LibroController, dondeestas operaciones deben ser implementadas, vemos implementada solo la acción “index”.Por lo tanto, sin necesidad de programar una aplicación completa, ya se tiene una aplicaciónfuncional para mostrar y probar. Tampoco es necesario invertir tiempo en diseñar la estructura de labase de datos, porque también es autogenerada por el framework. Esto es gran parte de la agilidadque agrega el framework al desarrollo de aplicaciones web.
  24. 24. 7. Estructura de las aplicaciones YuppSiguiendo con el ejemplo de la aplicación “biblioteca”, en esta sección veremos la estructura internade las aplicaciones de Yupp. La estructura generada por el framework para la aplicación “biblioteca”es la siguiente: ● apps ○ biblioteca ■ bootstrap ■ config ■ controllers ■ i18n ■ model ■ services ■ utils ■ views ■ app.xml/appsDirectorio donde se ubican las aplicaciones instaladas en el framework, tanto para desarrollo comopara su uso por usuarios finales./apps/bibliotecaDirectorio de la aplicación “biblioteca”./apps/biblioteca/bootstrapDirectorio donde se ubican los scripts de arranque de la aplicación. Estos scripts sirven para agregardatos en la base de datos para la correcta ejecución de la aplicación, por ejemplo se pueden dar dealta usuarios administradores./apps/biblioteca/configDirectorio donde se colocará todo recurso relativo a la configuración de la aplicación./apps/biblioteca/controllersDirectorio que contienen los controladores de la aplicación./apps/biblioteca/i18nDirectorio donde se colocarán los scripts de traducción (internacionalización) de la aplicación./apps/biblioteca/modelDirectorio que contiene las clases del modelo de información persistente de la aplicación./apps/biblioteca/servicesDirectorio que contiene las clases que implementan la lógica de negocio de la aplicación.
  25. 25. /apps/biblioteca/utilsDirecorio donde se coloca todo recurso auxiliar para el correcto funcionamiento de la aplicación./apps/biblioteca/viewsDirectorio que contiene las vistas de la aplicación, estas contienen toda la lógica de la interfaz deusuario de la aplicación./apps/biblioteca/app.xmlDescriptor de aplicaciones, que contiene metadatos de la aplicación que son accedidos por elframework y se utilizarán en el futuro para gestionar aplicaciones.La estructura mínima para las aplicaciones Yupp podría considerarse similar a la siguiente: ● boostrap ● controllers ● model ● views ● app.xmlTodos los demás directorios que no se utilicen, pueden ser eliminados.7.1 Configuración de la base de datos por aplicaciónEn la sección 3.3 se mostró cómo configurar la base de datos en Yupp. Esta configuración es global,o sea que todas las aplicaciones utilizarán la misma base de datos, con la misma configuración. Estono siempre es deseable, por lo que Yupp soporta la configuración de bases de datos por aplicación.7.1.1 Creando la configuraciónPor ejemplo, si quisiéramos que la aplicación “biblioteca” tuviera su propia base de datos,deberíamos seguir los siguientes pasos: 1. Si el directorio “config” no existe dentro de “apps/biblioteca”, crearlo. 2. Dentro de “apps/biblioteca/config”, crear el archivo “db_config.php” 3. Dentro del archivo, colocar el siguiente código PHP:$db = array( type => mysql, url => localhost, user => root, pass => , database => yupp_biblioteca );
  26. 26. El array $db contiene todos los datos de configuración de la base de datos para la aplicación“biblioteca”. En la clave “type” debe ponerse alguno de estos valores: “mysql”, “sqlite” o “postgres”.Estos valores están definidos en “core/config/core.config.YuppConfig.class.php”. En el futuro cuandose soporten otros motores de bases de datos, también podrá elegirse entre ellos. La clave “url” indicael servidor donde se encuentra instalado el motor de bases de datos. Las claves “user” y “pass” sonusadas para poder acceder a la base de datos. Y por último, la clave “database” define el nombre dela base de datos que estará utilizando nuestra aplicación.No es necesaria ninguna otra configuración. Si se cumplen las convenciones de nombrado yubicación de los archivos, Yupp framework por sí solo encontrará la configuración y la utilizará.7.2 Script de bootstrapOpcionalmente, cada aplicación puede tener un script de boostrap (arranque). Este script seejecutará una sola vez luego de instalar la aplicación dentro del framework, y servirá para dar de altainformación necesaria para el correcto funcionamiento de dicha aplicación, como por ejemplo losusuarios administradores de la aplicación.El script de bootstrap debe estar situado en el directorio “bootstrap” de la aplicación, y dentro deldirectorio un archivo llamado “apps.biblioteca.bootstrap.Bootstrap.script.php” (este sería el nombredel script de bootstrap para la aplicación “biblioteca”).Se tienen planes para que en el futuro pueda haber un script de boostrap para cada modo deejecución (desarrollo, producción y testing).7.3 Scripts de testingHoy Yupp Framework soporta la definición y ejecución de casos de testing. Este soporte es elmínimo necesario para estos fines. Se tienen planificadas varias mejoras para el área de testing.Los casos de testing se definen dentro del directorio “tests” de las aplicaciones Yupp. Un caso detesting es una clase que hereda de TestCase (clase provista por el framework), donde es necesariodefinir el método run(). Por ejemplo, la implementación de un caso de test podría ser el siguiente:YuppLoader::load(core.testing, TestCase);class TestCase001 extends TestCase { private $var = 0; public function run() { $this->test1(); $this->test2(); $this->reset(); }
  27. 27. public function test1() { $this->var++; $this->assert( $this->var == 1, Test igual a 1 ); $this->assert( $this->var != 0, Test distinto de 0 ); } public function test2() { $this->assert( is_numeric($this->var, Test is_numeric ); } public function reset() { $this->var = 0; }}El caso de testing debe implementar el método run(), luego dentro de este se invocan todos losmétodos definidos por el programador, que son los que implementan el caso de testing. No hayrestricciones sobre los nombres de los métodos (aparte del método run()).
  28. 28. 8. Model: funcionalidades avanzadasEn la sección 1.2 se tuvo una introducción al ORM de Yupp, mostrando cómo se definían las clasesdel modelo y los distintos tipos de relaciones entre estas clases. También se mencionó cómo esasclases y relaciones eran mapeadas a una estructura de base de datos. En esta sección se veránotros aspectos interesantes que complementan los ya vistos.8.1 Gestionando relaciones hasOneSupongamos que la clase Libro que definimos previamente en la sección 6, ahora en lugar de unatributo “autor” tiene una relación hasOne a una clase Autor que definimos más abajo:YuppLoader::load("biblioteca.model", "Autor");class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("titulo", Datatypes :: TEXT); $this->addAttribute("genero", Datatypes :: TEXT); $this->addAttribute("fecha", Datatypes :: DATETIME); $this->addAttribute("idioma", Datatypes :: TEXT); $this->addAttribute("numeroPaginas", Datatypes :: INT_NUMBER); $this->addHasOne("autor", "Autor"); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}
  29. 29. Una forma sencilla de relacionar una instancia de Libro a una instancia de Autor es en la propiaconstrucción de las instancias:$libro = new Libro( array( "titulo" => "El ingenioso hidalgo don Quixote de la Mancha", "genero" => "prosa narrativa", "fecha" => "1605-01-01", "idioma" => "es", "numeroPaginas" => 223, "autor" => new Autor( array( "nombre" => "Miguel de Cervantes Saavedra", "fechaNacimiento" => "1547-09-29" ) ) ));Otra forma de asociación es mediante el método dinámico “getX”, donde “X” es el nombre de unatributo, por ejemplo el atributo hasOne. Entonces podríamos tener el siguiente código:$libro = new Libro( array( "titulo" => "El ingenioso hidalgo don Quixote de la Mancha", "genero" => "prosa narrativa", "fecha" => "1605-01-01", "idioma" => "es", "numeroPaginas" => 223 ));$autor = new Autor( array( "nombre" => "Miguel de Cervantes Saavedra", "fechaNacimiento" => "1547-09-29" ));$libro->setAutor( $autor );Para obtener el autor de una instancia de Libro, se utiliza el método dinámico “getXXX”, donde “XXX”es el nombre de un atributo, por ejemplo:$autor = $libro->getAutor();
  30. 30. 8.2 Gestionando relaciones hasManyPara seguir con el ejemplo de los libros, supongamos que ahora Libro tiene una relación hasManypara los coautores:YuppLoader::load("biblioteca.model", "Autor");class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("titulo", Datatypes :: TEXT); $this->addAttribute("genero", Datatypes :: TEXT); $this->addAttribute("fecha", Datatypes :: DATETIME); $this->addAttribute("idioma", Datatypes :: TEXT); $this->addAttribute("numeroPaginas", Datatypes :: INT_NUMBER); $this->addHasOne("autor", "Autor"); $this->addHasMany("coautores", "Autor"); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}Si se tiene una instancia de Libro y dos instancias de Autor, tales que los autores son coautores dellibro, para asociar los coautores al libro se utiliza el método dinámico “addToX”, donde “X” es elnombre de una relación hasMany, por ejemplo:$libro = new Libro(...);$coautor1 = new Autor(...);$coautor2 = new Autor(...);$libro->addToCoautores( $coautor1 );$libro->addToCoautores( $coautor2 );Para obtener todos los coautores de la instancia de Libro se utiliza el método dinámico “getX” al igualque en el caso previo para obtener la instancia en la relación hasOne.$coautores = $libro->getCoautores();Para quitar un coautor del libro se utiliza el método dinámico “removeFromX”, donde “X” es elnombre del atributo hasMany. Para invocar a esta operación, la instancia a remover de la relacióndebe haber sido persistida en la base de datos, porque es necesario que la instancia tengaidentificador. El identificador solo se establece cuando la instancia es almacenada en la base dedatos.$libro->removeFromCoautores( $coautor1 );En este ejemplo es útil recalcar que $coautor1 es removido de la relación hasMany con $libro, peroque la instancia $coautor1 no es eliminada y sigue persistida en la base de datos.
  31. 31. 8.2.1 Tipos de relaciones hasManyEl ORM de Yupp soporta tres tipos distintos de comportamientos para las relaciones hasMany. Estasrelaciones pueden comportarse como colecciones, listas o conjuntos.ColeccionesPor defecto las relaciones hasMany tienen este comportamiento, que consiste en no tener ningúntipo de restricción sobre lo que se pone dentro de la relación. Los objetos no tienen orden ni severifican duplicados. Ejemplo:$this->addHasMany("coautores", Autor, PersistentObject::HASMANY_COLECTION);ListasUna relación hasMany definida con comportamiento de lista, almacena el orden con que los objetosson agregados a la relación. Esto sirve para obtener instancias persistentes, con el mismo orden enel que fueron persistidas. Ejemplo:$this->addHasMany("coautores", Autor, PersistentObject::HASMANY_LIST);ConjuntosLas relaciones hasMany definidas como conjuntos, implementan la restricción de verificación porduplicados. Si en una relación hasMany definida como conjunto se intenta agregar dos veces elmismo objeto, el segundo no será agregado. Esto funciona si los objetos que se agregan fueronpersistidos previamente. Ejemplo:$this->addHasMany("coautores", Autor, PersistentObject::HASMANY_SET);Nota: como el comportamiento por defecto es de colección, el primer ejemplo se comporta de igualforma que el siguiente código, sin pasar el tercer parámetro:$this->addHasMany("coautores", Autor);
  32. 32. 8.3 Convenciones sobre nombrado de tablasEn la sección 1.2 se vio un ejemplo de clase persistente, donde se declaraba explícitamente elnombre de la tabla en la base de datos donde las instancias de esa clase iban a ser persistidas. Enesta sección vamos a explicar cuáles son las convenciones en cuanto a los nombres de las tablas enla base de datos.8.3.1 Nombrado implícito de tablasSe toma como nombrado implícito cuando no se define el nombre de la tabla de forma explícita en elconstructor de la clase persistente. En el caso que se vio en la sección 1.2, el nombrado de la tablaera explícito, haciendo la siguiente invocación:$this->setWithTable("nombre_de_la_tabla");En el caso de la aplicación “biblioteca” con la que venimos trabajando, la clase Libro no hace unnombrado explícito. Por lo tanto se aplica la siguiente convención de Yupp:Cuando no hay nombrado explícito de la tabla donde se persistirán las instancias de cierta clase delmodelo de información de una aplicación Yupp, el nombre de la tabla será igual al nombre de laclase en minúsculas.O sea que para los siguientes nombres de clases, tendremos los siguientes nombres de tablas: ● Libro => libro ● BuenLibro => buenlibro ● BuenLibro2 =>buelibro28.3.2 Nombrado explícito de tablasEn la sección anterior se comentó que es el nombrado explícito, que consiste en llamar al método“setWithTable” con un determinado nombre de tabla. A continuación veremos distintos ejemplos paralos nombres de tablas y como Yupp reacciona ante ellos. Una restricción a considerar es que elnombre de la tabla no puede contener caracteres que sean usados por los motores de bases dedatos, como por ejemplo el punto (“.”). Yupp no hace ningún tipo de verificación de estos caracteres,por lo que el programador debe tener especial cuidado. ● Nombre de la tabla: “libro”, nombre aceptado por Yupp: “libro” ● Nombre de la tabla: “Libro”, nombre aceptado por Yupp: “libro” ● Nombre de la tabla: “BuenLibro”, nombre aceptado por Yupp: “buenlibro” ● Nombre de la tabla: “Buen_Libro”, nombre aceptado por Yupp: “buen_libro” ● Nombre de la tabla: “Buen Libro”, nombre aceptado por Yupp: “buen_libro”
  33. 33. Las reglas de Yupp sobre los nombres especificados de forma explícita son: ● El nombre especificado se convierte a minúsculas. ● Si el nombre especificado tiene espacios, se convierten a guiones bajos.8.3.3 Nombrado de tablas de joinCuando una relación hasMany es definida, como se hizo previamente en la clase Libro, hacia laclase Autor, con una relación llamada “coautores”, una tabla intermedia es creada para mantener lasrelaciones. En el ejemplo de la aplicación “biblioteca”, esta tabla intermedia es una tabla de join quesirve para persistir y obtener todos los coautores de un determinado libro.El nombre de la tabla de join para el caso mencionado será “libro_coautores_autor”, debido a que laclase donde se define la relación se persiste en la tabla “libro”, a que la clase relacionada se persisteen la tabla “autor”, y a que la relación se llama “coautores”.8.4 Creando restricciones sobre campos y relacionesLas clases persistentes permiten la definición de restricciones sobre los campos declarados en dichaclase.8.4.1 Definiendo restriccionesTomando el ejemplo de la clase Autor, de la aplicación “biblioteca”, podemos definir que el nombredel autor no puede ser vacío. Existen dos formas de hacerlo, la primera es usando la restricción“blank”. Aquí el ejemplo completo:class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); $this->addConstraints("nombre", array( Constraint::blank(false) )); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}Entonces, utilizamos blank(false) para indicar que el campo “nombre” no puede ser vacío. Esnecesario diferenciar el caso de un string vacío del caso de un sting null. Si se desea que el nombredel autor tampoco sea null, debe especificarse una restricción como la siguiente:
  34. 34. // Restricciones $this->addConstraints("nombre", array( Constraint::nullable(false), Constraint::blank(false) ));Mediante la restricción “minLength” que restringe la cantidad de caracteres mínima que debe tenerun campo de tipo TEXT. Por ejemplo podríamos usar la siguiente restricción para indicar que elnombre debe tener por lo menos 1 carácter y lograr el mismo comportamiento que con“blank(false)”.: // Restricciones $this->addConstraints("nombre", array( Constraint::minLength(1) ));Si quisiéramos restringir el largo máximo del nombre, por ejemplo a 100 caracteres, utilizaríamos larestricción “maxLength”: // Restricciones $this->addConstraints("nombre", array( Constraint::blank(false), Constraint::maxLength(100) ));Como vemos, es posible definir varias restricciones para el mismo campo. Ahora, si quisiéramosdefinir restricciones para campos numéricos, por ejemplo el campo “numeroPaginas” de la claseLibro, podríamos decir que un libro debe tener como mínimo 20 páginas y como máximo 3000: // Restricciones $this->addConstraints("numeroPaginas", array( Constraint::min(20), Constraint::max(3000) ));Este par de restricciones podría expresarse como una sola restricción “between”, la cual indica queel valor de un campo debe estar entre dos valores datos, de la siguiente forma: // Restricciones $this->addConstraints("numeroPaginas", array( Constraint::between(20, 3000) ));Para ver todas las restricciones disponibles acceder a:http://code.google.com/p/yupp/source/browse/YuppPHPFramework/core/validation/core.validation.Constraints.class.php
  35. 35. 8.4.2 Verificando restriccionesExisten dos formas de verificar restricciones sobre los valores que tienen los campos de una clasepersistente. La primera es utilizando el método “validate” de PersistentObject, la segunda esutilizando el método “save” de PersistentObject.8.4.2.1 Validando datos mediante restriccionesEl siguiente es un ejemplo de validación utilizando el método validate($validateCascade = false)$autor = new Autor();$autor->setNombre("");if (!$autor->validate()) print_r( $autor->getErrors() );Como al crear la instancia de Autor, se se establece el valor del campo nombre en un string vacío,violara la restricción de no vacío que fue definida previamente. Por lo tanto, “validate” devolverá“false” y mediante el método “getErrors” se obtendrán los errores de validación, y lo que se imprimeserá parecido a esto:Array ( [nombre] => Array ( [0] => El valor del atributo nombre no puede ser vacio ))El método “validate” puede recibir opcionalmente un booleano que indica si la validación debehacerse en cascada, o sea, si debería ejecutarse sobre la instancia actual y sobre las instancias deotras clases que tenga relacionadas. Por defecto la validación no se ejecuta en cascada.8.4.2.2 Validando datos previa a la persistenciaDe forma análoga a “validate”, al ejecutar el método “save” también se validan los datos. Esto quieredecir que cada vez que se desee persistir una instancia de una clase, internamente se invoca almétodo “validate”. En el caso que los valores de los campos de la instancia violen alguna de lasrestricciones definidas en la clase, la instancia no es persistida y se generan todos los mensajes deerror para cada campo y cada restricción violada. Esto último es accesible mediante el método“getErrors”. Por lo tanto, el siguiente código producirá el mismo resultado que el del ejemplo anterior(notar que se invoca a save y no a validate):$autor = new Autor();$autor->setNombre("");if (!$autor->save()) print_r( $autor->getErrors() );
  36. 36. 8.5 Definiendo lógica pre-validaciónLa clase PersistentObject tiene un método protegido “preValidate” (para ser implementadoopcionalmente por sus subclases), el cual es ejecutado previamente a la validación. Este método fueideado para modificar los valores de los campos de la clase, previamente a la ejecución de lavalidación. Esto tiene varias utilidades.Validación forzada de restriccionesSi en el método “preValidate” se detecta que un campo no cumple con cierta restricción, o sea que alverificar la restricción el valor no cumplirá con dicha restricción, se puede modificar el valor delcampo para que cumpla con la restricción y así no se generen errores de validación. Un ejemplopodría ser si un valor es null, teniendo una restricción “nullable(false)”, y en “preValidate” se cambiael valor por un string vacío.Limpieza de valoresEs común que cuando un usuario ingresa algún texto, este venga con espacios o fines de líneaextras al principio o al final. Si ese tipo de valores son asignados en campos de texto de una clasepersistente, dentro del método “preValidate” podría implementarse una limpieza del texto.Esto podría servir también para aumentar la seguridad sobre lo que se inserta en la base de datos,ya que un texto podría tener caracteres inválidos, o incluso código javascript o SQL ingresado por unusuario malicioso, esto también puede detectarse y limpiarse en el método “preValidate”. Acontinuación se muestra un ejemplo que quita espacios extra que pueden venir para el campo“nombre” del autor.class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { // Campos $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); // Restricciones $this->addConstraints("nombre", array( Constraint::blank(false) )); parent :: __construct($args, $isSimpleInstance); } protected function preValidate() { $nombre = $this->getNombre(); if ( !is_null($nombre) ) $this->setNombre( trim($nombre) ); } // Métodos estáticos omitidos}
  37. 37. 8.6 Usando belongsToEn el ORM de Yupp, hay varias operaciones que pueden realizarse en cascada, o sea que seaplican sobre una instancia de cierta clase persistente, y sobre las instancias de otras clasespersistentes que tenga asociada. Para esto se necesita la propiedad “belongsTo”, que indica cuál esel lado fuerte y cuál el débil en una relación entre dos clases. Por ejemplo, si se tienen dos clases Ay B, “A hasOne B” y “B belongsTo A”, el lado fuerte de la relación es A y el débil es B.Esta propiedad de las relaciones entre clases puede ser definida de forma explícita, o Yupp puedeinferirla mediante convenciones. A continuación se muestras todos los tipos de relaciones entre 2clases A y B, y cuál lado es considerado fuerte y cuál débil por Yupp en el caso de no definir deforma explícita la propiedad belongsTo.Notación: ● A(*)->(1)B: indica que la clase A tiene una relación unidireccional a B, con A hasOne B. ● A(*)<->(1)B: indica que la clase A tiene una relación bidireccional a B, con A hasOne B y B hasMany A.Casos: 1. A(*)->(1)B: Yupp no sabe cual lado es el fuerte, se necesita belongsTo explícito. 2. A(1)<->(1)B: Yupp no sabe cual es el lado fuerte, se necesita belongsTo explícito. 3. A(1)->(*)B: Yupp considera que B belongsTo A 4. A(1)<->(*)B: Yupp considera que B belongsTo A 5. A(*)->(*)B: Yupp considera que B belongsTo A 6. A(*)<->(*)B: Yupp no sabe cual es el lado fuerte, se necesita belongsTo explícito.Para ejemplificar estos casos, volvamos a la aplicación biblioteca, donde el modelo es el siguiente:class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { // Campos $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); // Restricciones $this->addConstraints("nombre", array( Constraint::blank(false) )); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}
  38. 38. class Libro extends PersistentObject { function __construct($args = array (), $isSimpleInstance = false) { $this->addAttribute("titulo", Datatypes :: TEXT); $this->addAttribute("genero", Datatypes :: TEXT); $this->addAttribute("fecha", Datatypes :: DATETIME); $this->addAttribute("idioma", Datatypes :: TEXT); $this->addAttribute("numeroPaginas", Datatypes :: INT_NUMBER); $this->addHasOne("autor", "Autor"); $this->addHasMany("coautores", "Autor"); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}O sea que: ● Libro hasOne Autor ● Libro hasMany Autor (coautores) ● No se ha declarado belongsTo explícito en ninguna claseCon este modelo, podemos ejecutar el siguiente caso de prueba:$libro = new Libro( array( "titulo" => "El ingenioso hidalgo don Quixote de la Mancha", "genero" => "prosa narrativa", "fecha" => "1605-01-01", "idioma" => "es", "numeroPaginas" => 223, "autor" => new Autor( array( "nombre" => "Miguel de Cervantes Saavedra", "fechaNacimiento" => "1547-09-29" )), "coautores" => array( new Autor( array( "nombre" => "Sancho Panza", "fechaNacimiento" => "1547-01-01" )), new Autor( array( "nombre" => "Quijote", "fechaNacimiento" => "1542-05-21" )) ) ));if( !$libro->save() ) print_r( $libro->getErrors() );
  39. 39. Al ejecutar este caso de prueba, veremos que en la base de datos se ha guardado el libro y suscoautores (en cascada), pero no se ha guardado su Autor. Esto se debe a que la relación “autor”declarada en la clase Libro, entra en el caso 1 de la inferencia de belongsTo por Yupp framework,por lo que si queremos que se persista también esa relación a Autor, debemos declarar unbelongsTo explícito en la clase Autor:YuppLoader::load("biblioteca.model", "Libro");class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->belongsTo = array( Libro ); // Campos $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); // Restricciones $this->addConstraints("nombre", array( Constraint::blank(false) )); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}Realizando este cambio en la clase Autor, el test anterior almacenará en cascada tanto al autorcomo a los coautores del libro. Si luego de ejecutar el caso de prueba de la sección 8.5, ejecutamosel siguiente caso de prueba, el primer autor tendrá entre sus libros al libro que lo tiene como autor:$libro = Libro::get(1); // Carga el libro con id=1 desde la base de datos$autor = Autor::get(1); // Carga el autor con id=1 desde la base de datos$autor->addToLibros($libro); // Agrega el libro a los libros del autorif( !$autor->save() ) print_r( $autor->getErrors() ); // Intenta persistir
  40. 40. 8.7 Definiendo relaciones de muchos a muchosDe la misma forma que se definen las relaciones de uno a muchos usando hasMany, se puedendefinir relaciones bidireccionales de muchos a muchos. Simplemente es necesario colocar unareferencia hasMany en cada una de las clases. Por ejemplo, si desde la clase Autor se quisierantodos los libros que escribió, ser podría hacer la siguiente modificación al modelo:class Autor extends PersistentObject{ function __construct($args = array (), $isSimpleInstance = false) { $this->belongsTo = array( Libro ); // Campos $this->addAttribute("nombre", Datatypes :: TEXT); $this->addAttribute("fechaNacimiento", Datatypes :: DATE); $this->addHasMany("libros", "Libro"); // Restricciones $this->addConstraints("nombre", array( Constraint::blank(false) )); parent :: __construct($args, $isSimpleInstance); } // Métodos estáticos omitidos}Como vimos en las reglas de belongsTo en la sección previa, para definir relaciones de muchos amuchos bidireccionales, uno de los lados debe tener declarado un belongsTo. En este caso dejamosdeclarado el belongsTo en el Autor.8.8 Eliminación física vs. eliminación lógicaSiempre que se quiera eliminar una instancia de una clase persistente, se debe ser cuidadoso con laestrategia de eliminado a utilizar. En el caso de la eliminación física, el o los registroscorrespondientes a la persistencia de la instancia a eliminar, serán borrados físicamente de la basede datos y no se podrán recuperar sus datos. En cambio, con la eliminación lógica, los registrossiguen existiendo, pero tienen una marca de “eliminados”, esa marca es utilizada internamente por elframework para saber que registros se encuentran activos y cuales fueron eliminados de formalógica.
  41. 41. El siguiente código elimina físicamente una instancia de Libro persistida en la base de datos:$libro = Libro::get(1); // Carga el libro con id=1 desde la base de datos$libro->delete(); // Eliminación físicaEl siguiente código elimina lógicamente una instancia de Libro persistida en la base de datos:$libro = Libro::get(1); // Carga el libro con id=1 desde la base de datos$libro->delete(true); // Eliminación física
  42. 42. 9. ControladoresCada aplicación Yupp puede tener un conjunto de controladores, los cuales procesan los pedidos delusuario, e implementan determinada lógica.Partiendo del ejemplo del controlador generado para la aplicación “biblioteca”, vamos a completarlopara mostrar los conceptos detrás de la programación de controladores.YuppLoader::load(biblioteca.model, Libro);class LibroController extends YuppController { public function indexAction() { return $this->renderString("Bienvenido a su nueva aplicacion!"); }}9.1 ConvencionesUbicación:Todos los controladores de una aplicación Yupp deben estar dentro el directorio “controllers” de laaplicación, por ejemplo “biblioteca/controllers”.Nombres:En Yupp, los nombres de todas las clases empiezan en mayúsculas. El nombre de las clases queimplementan controladores debe terminar en “Controller”, por ejemplo “LibroController” o“AutorController”.Acciones:Las acciones de un controlador son métodos especiales que pueden ser invocados mediante elenvío de una URL. En la sección 5 vimos cómo están formadas las URLs dentro de Yupp, y que unaparte de estas determina la acción a ejecutar. Un controladores puede además implementar métodosque no son acciones, por ejemplo métodos auxiliares que son usados por las acciones.Los nombres de los métodos que implementan acciones son públicos, comienzan en minúsculas yterminan en “Action”. Además no reciben parámetros de forma explícita. Previamente vimos unejemplo con la acción “index” del controlador LibroController.Retorno de accionesLas acciones pueden tener varios tipos de retorno. En el ejemplo previo se vio un retorno del tipo“renderString”, que simplemente muestra en pantalla un determinado texto. En general no vamos aquerer retornar un texto, si no una vista. Más adelante veremos como crear vistas, para esta secciónes suficiente con saber que una vista tiene un determinado nombre y que recibe un conjunto deparámetros (modelo) que utiliza para generar una página web que se le mostrará al usuario.
  43. 43. Si una acción simplemente retorna sin un valor, Yupp intentará mostrar una vista que tenga al mismonombre de la acción que se está ejecutando. Por ejemplo, el siguiente código intentará mostrar unavista llamada “index” (obviamente, si la vista no existe, ocurrirá un error). public function indexAction() { return; }Una segunda opción, es especificar explícitamente el nombre de la vista. En este caso, el nombre dela vista podría ser distinto al de la acción, pero igualmente intentaremos mostrar la vista “index”. Estecódigo logra el mismo efecto que el anterior. public function indexAction() { return $this->render("index"); }Digamos ahora que la vista “index” necesita algunos parámetros para mostrarse correctamente. Elcontrolador puede pasarle un conjunto de parámetros al retornar, por ejemplo el siguiente códigointentará mostrar una vista con el mismo nombre que la acción, o sea “index”, y recibirá un conjuntode valores. public function indexAction() { return array( valor1 => 29, valor2 => hola ); }Redirigiendo pedidosAl terminar de ejecutar una acción, esta podría no decidir mostrar una vista, si no realizar unaredirección para ejecutar otra acción. Esto genera un nuevo pedido HTTP reentrante. A continuaciónse muestra un ejemplo de acción que redirige el pedido a otra acción: public function indexAction() { return $this->redirect( array( "controller" => "autor", "action" => "show", "params" => array( "id" => 101, "mensaje" => "Hola mundo" ) ) ); }Si esta fuera la acción “index” de LibroController, lo que haría es redirigir el pedido a la acción “show”de AutorController, enviándole como parámetros “id=101” y “mensaje=Hola mundo”. Los pedidosHTTP reentrantes son enviados usando el método GET de HTTP, por lo tanto, el pedido sería parala siguiente URL de Yupp: /yupp/biblioteca/autor/show?id=101&mensaje=Hola mundo
  44. 44. 9.2 Acciones de infraestructura (scaffolded)Como vimos en la sección 6, en el controlador LibroController podemos ejecutar acciones que noestán implementadas, como por ejemplo “list”, “show”, “edit” y “create”. Estas acciones seencuentran implementadas en YuppController, la superclase de todos los controladores. En generales suficiente la implementación por defecto, pero de ser necesario ejecutar cierta lógica particular,estas acciones pueden ser implementadas en nuestros controladores. Un caso típico es cuando uncontrolador no tiene ninguna clase del modelo de información asociada, por ejemplo LibroControllerse encarga de las acciones sobre la clase Libro. Estas acciones se consideran de “infraestructura”por que son acciones genéricas, capaces de ser aplicadas a cualquier clase del modelo persistentede cualquier aplicación Yupp.Existe otro conjunto de acciones que están implementadas en CoreController, el controlador queimplementa las acciones que se realizan desde el escritorio de Yupp. Por ejemplo, si accedemos a laacción de infraestructura “list” del controlador LibroController mediante la siguiente URL, y luegovamos al “show” del un libro haciendo clic en su identificador (crear uno si no hay ninguno), vamos aver un link hacia su autor relacionado (si tiene uno), haciendo clic ahí veremos los detalles del autor,aunque no exista el controlador relacionado a la clase Autor.Comenzamos aquí: http://localhost/yupp/biblioteca/libro/listTerminamos aquí: http://localhost/yupp/core/core/show?app=biblioteca&class=Autor&id=1Nota: para tener datos para probar, puedes ejecutar el test que crea un libro y le asocia un autor.9.3 Recibiendo archivosUn tipo especial de parámetro que puede ser recibido por el método POST de HTTP son losarchivos. Un usuario puede subir un archivo usando un formulario como este en una vista:<form method="post" enctype="multipart/form-data"> <input type="file" name="archivo" value="" /></form>Del lado del servidor, supongamos que el controlador que recibe el archivo, tiene una acción con elsiguiente código:public function recibeArchivoAction(){ print_r($this->params);}
  45. 45. Esta acción mostrará algo similar a esto:Array ( [file] => Array ( [name] => HCE 004.jpg [type] => image/jpeg [tmp_name] => C:wamptmpphp77.tmp [error] => 0 [size] => 162096 ))Si quisiéramos cargar el contenido del archivo subido al servidor en una variable, por ejemplo paraguardarlo en la base de datos:public function recibeArchivoAction(){ $filename = $this->params["tmp_name"]; $imagen = FileSystem::read($filename);}Y si quisiéramos copiar el archivo a una ubicación determinada:public function recibeArchivoAction(){ $filename = $this->params["tmp_name"]; $ubicacion = "dir/subdir/nombreArchivo.jpg"; if (move_uploaded_file($filename, $ubicacion)) { echo "El archivo ha sido cargado correctamente."; } else { echo "Ocurrió un error al subir el archivo. No pudo guardarse."; }}
  46. 46. 9.4 Devolviendo XML o JSONPreviamente vimos como devolver un string desde una acción. El mismo mecanismo puede serusado para devolver un string XML o JSON. Estos serán casos de acciones que no devuelven vistas,si no que implementan servicios para ser consumidos desde la interfaz de usuario (por ejemplomediante pedidos AJAX) o desde otros sistemas.9.4.1 Programando una acción que devuelve JSONDigamos que dentro de LibroController necesitamos una acción que devuelva los datos de undeterminado libro, pero lo queremos en notación JSON. Para lograrlo, podríamos implementar laacción de la siguiente manera: public function jsonShowAction() { YuppLoader::load(core.persistent.serialize, JSONPO); $id = $this->params[id]; // Obtiene el parámetro id $libro = Libro::get( $id ); // Carga el libro con ese id $json = JSONPO::toJSON( $libro ); // Genera la serialización a json // Lo que se devolverá en el response HTTP será de tipo json header(Content-type: application/json); // Escribe el string json en la respuesta al usuario return $this->renderString( $json ); }La URL Yupp para invocar a esta acción, sería: http://localhost/yupp/biblioteca/libro/jsonShow?id=1Lo que obtendremos será algo así: { titulo: "El ingenioso hidalgo don Quixote de la Mancha" genero: "prosa narrativa" fecha: "1605-01-01 00:00:00" idioma: "es" numeroPaginas: "223" class: "Libro" deleted: "" autor_id: "1" id: "1" }
  47. 47. 9.4.2 Programando una acción que devuelve XMLAnálogamente al ejemplo de JSON, podemos necesitar programar una acción que devuelva XML. public function xmlShowAction() { $id = $this->params[id]; $libro = Libro::get( $id ); $xml = XMLPO::toXML( $libro ); header(Content-type: text/xml); return $this->renderString( $xml ); }La URL Yupp para invocar a esta acción, sería: http://localhost/yupp/biblioteca/libro/xmlShow?id=1Y el resultado es parecido a este XML:<Libro> <titulo>El ingenioso hidalgo don Quixote de la Mancha</titulo> <genero>prosa narrativa</genero> <fecha>1605-01-01 00:00:00</fecha> <idioma>es</idioma> <numeroPaginas>223</numeroPaginas> <class>Libro</class> <deleted/> <autor_id>1</autor_id> <id>1</id></Libro>
  48. 48. 10. VistasLas vistas implementan la interfaz de usuario de las aplicaciones Yupp. Es común que lasaplicaciones tengan una vista para listar instancias de cierta clase persistente, otra vista para ver losdetalles de una instancia, otra para crear nuevas instancias y otra para editar instancias. En estasección veremos los distintos aspectos involucrados en la creación de este tipo de vistas y de otrostipos de vistas más complejos.10.1 Fundamentos para la implementación de vistasPrimero que nada, las vistas no son más que scripts PHP que solo contienen lógica de interfaz deusuario, o sea que: ● Una vista no debería contener lógica de negocio ● Una vista no debería contener consultas complejas a la base de datos ● Una vista no debería modificar el estado de la aplicaciónLas vistas son colocadas en el directorio “views” de las aplicaciones Yupp, con la característica deque las vistas se organizan según los controladores que las muestran. Por ejemplo, si para elcontrolador LibroController, se tuviera una vista llamada “list”, esta vista estaría implementada en lasiguiente ubicación: apps/biblioteca/views/libro/list.view.phpLa convención de nombrado de vistas es simple: el archivo debe terminar en “.view.php”.A continuación veremos un ejemplo de una vista para el listado de libros:<?php // Modelo pasado desde el controlador $m = Model::getInstance();?><html> <head> <style> table { border: 1px solid; } td { border: 1px solid; padding: 5px; } </style> </head> <body> <h1>Libros</h1>
  49. 49. <table> <!-- El controlador puso la lista de libros en la clave libros --> <?php foreach( $m->get(libros) as $libro) : ?> <tr> <td><?php echo $libro->getTitulo(); ?></td> <td><?php echo $libro->getGenero(); ?></td> <td><?php echo $libro->getIdioma(); ?></td> </tr> <?php endforeach; ?> </table> </body></html>La acción del controlador que especifica que se debe mostrar la vista anterior, puede ser algo así: public function listAction() { // Carga todos los libros desde la base de datos $libros = Libro::listAll($this->params); // Muestra la vista list enviándole los libros como modelo return array(libros => $libros); }10.2 Uso de helpersLos helpers son componentes reusables de código que implementan lógica de interfaz de usuario.Yupp implementa varios helpers que pueden ser utilizados en la programación de vistas, pero elprogramador puede definir sus propios helpers cuando los necesite. En las siguientes seccionesveremos algunos helpers interesantes. Por una lista completa de helpers, puedes visitar ladocumentación del proyecto: http://www.simplewebportal.net/yupp_framework_php_doc10.2.1 Helper layoutUn layout sirve para reutilizar una estructura general entre varias vistas para la interfaz de usuario deuna aplicación. La forma de especificar que una vista utiliza un layout es mediante un etiqueta. Porejemplo, si la vista “list” del controlador LibroController (que vimos anteriormente) usara un layoutllamado “default”, el código de la vista sería el siguiente:<?php // Modelo pasado desde el controlador $m = Model::getInstance();?><html> <layout name="default" /> <head> <style> table {
  50. 50. border: 1px solid; } td { border: 1px solid; padding: 5px; } </style> </head> <body> <h1>Libros</h1> <table> <!-- El controlador puso la lista de libros en la clave libros --> <?php foreach( $m->get(libros) as $libro) : ?> <tr> <td><?php echo $libro->getTitulo(); ?></td> <td><?php echo $libro->getGenero(); ?></td> <td><?php echo $libro->getIdioma(); ?></td> </tr> <?php endforeach; ?> </table> </body></html>Importante: la etiqueta que indica el layout a utilizar debe ir inmediatamente después de la etiqueta“html”.El código del layout puede ser similar a el siguiente, donde $head es todo lo que está dentro de laetiqueta “head” de la vista que referencia al layout, y $body es todo lo que está dentro de la etiqueta“body” de la vista que referencia al layout.<html> <head> <style type="text/css"> ... </style> <?php echo $head; ?> </head> <body> ... <div style="padding:10px;"><?php echo $body; ?></div> </body></html>Los layouts son también scripts PHP, pero a diferencia de las vistas, deben cumplir con lassiguientes convenciones: ● Deben mostrar a las variables $head y $ body. ● El archivo debe terminar en “.layout.php”. ● El archivo debe ubicarse en el directorio “views” de la aplicación.10.2.2 Helper template
  51. 51. Los templates son otra forma de reutilizar código entre distintas vistas, pero a diferencia del layoutque es para reutilizar la estructura general de la vista, los templates reutilizan lógica de interfaz deusuario interna a la vista. Por ejemplo si para mostrar el mismo objeto (podría ser una instancia deLibro) se utilizara la misma lógica en varias vistas, esa lógica podría ponerse dentro de un template,y reutilizar el template en dichas vistas. A continuación se muestra un ejemplo de cómo puede seruna llamada a un template que muestra un libro con cierto formato:Helpers::template( array("controller" => "libro", "name" => "details", "args" => array("libro" => $libro) ) );Los templates también son scripts PHP cuyo nombre termina en “.template.php”, por ejemplo, eltemplate referenciado desde el código anterior es “libro.template.php”. Este template podrá tenercódigo PHP, HTML y de otros tipos. Por ejemplo, el template “libro” podría ser así:<div> <b><?php echo $libro->getTitulo(); ?></b> (<?php echo $libro->getGenero(); ?>, <?php echo $libro->getIdioma(); ?>)</div>Entonces podríamos cambiar el código de la vista “list” (vista en la sección 10.2.1) para que utilice eltemplate:<?php $m = Model::getInstance(); // Modelo pasado desde el controlador?><html> <layout name="default" /> <head> <style> table { border: 1px solid; } td { border: 1px solid; padding: 5px; } </style> </head> <body> <h1>Libros</h1> <!-- El controlador puso la lista de libros en la clave libros --> <?php foreach( $m->get(libros) as $libro) : ?> <!-- Se le pasa el libro al template --> <?php Helpers::template( array( "controller" => "libro", "name" => "details", "args" => array("libro" => $libro) ) ); ?> <?php endforeach; ?> </body>
  52. 52. </html>10.2.3 Helper javascriptEl helper javascript es útil para controlar la inclusión de scripts javascript. Una posible llamada es lasiguiente:<?php echo h("js", array("name" => "jquery/jquery-1.5.1.min") ); ?>El código PHP previo, generará el siguiente código HTML:<script type="text/javascript" src="/yupp/js/jquery/jquery-1.5.1.min"></script>10.2.4 Helper ajax linkEl helper ajax link, sirve para crear links que al cliquearlos envían un pedido HTTP a unadeterminada acción de un controlador, con la característica de que el pedido va por AJAX. Estoayuda a crear aplicaciones web más interactivas. Un ajax link podría ser creado de la siguientemanera:<?php echo Helpers::ajax_link( array( "app" => "biblioteca", "controller" => "libro", "action" => "jsonShow", "id" => $libro>getId(), "body" => "Obtener comentarios por Ajax", "after" => "after_function", "before" => "before_function" ) ); ?>El código PHP previo, generará el siguiente código HMTL/Javascript:<script type="text/javascript">function ajax_link_0() { $.ajax({ url: /yupp/biblioteca/libro/jsonShow?id=1, beforeSend: before_function, success: after_function });}</script><a href="javascript:ajax_link_0()" target="_self" ">Obtener datos por Ajax</a>El código previo indica que al hacer clic sobre el link, se llama a una función Javascript llamada“before_function”, y al recibir la respuesta del servidor, se llama a la función “after_function”. Ambasfunciones deben ser implementadas por el programador, como se muestra a continuación:<script type="text/javascript">
  53. 53. // Handlers para JQuery var before_function = function(req, json) { $(#estado).html( "Cargando..." ); } var after_function = function(json) { var libro = json; alert(libro.titulo + (+ libro.genero +)); $(#estado).html( "" ); }</script>Una característica es que el código Javascript generado, dependerá de la librería Javascript que elprogramador referencie, esto quiere decir que la implementación de ajax link varía según si elprogramador usa Prototype o jQuery. Esto es transparente al programador.10.3 Vistas de infraestructura (scaffolded)De la misma forma que las acciones básicas de los controladores (list, show, create, edit, delete)están disponibles sin necesidad de programarlas, las vistas relacionadas a las acciones list, show,create y edit, también son generadas por el framework, sin necesidad de programarlas.Para indicarle a Yupp que se quiere utilizar una vista particular en lugar de las vistas deinfraestructura, simplemente se debe colocar el archivo de la vista, cumpliendo las convenciones denombrado y ubicación. No es necesario realizar ningún tipo de configuración extra.
  54. 54. 11. Estado actual del proyecto11.1 Hoja de ruta del proyectoEl proyecto tiene un camino marcado, con el objetivo de lograr una solución robusta, pequeña,rápida y completa para el desarrollo de aplicaciones web sobre PHP. Ese camino ya está avanzado,y este tutorial es prueba de ello. Para seguir el avance, y saber en qué puntos se están trabajando,en nuestra wiki hay una “hoja de ruta”, donde se actualizan los cambios y mejoras realizadas alframework, para cada versión del mismo. http://code.google.com/p/yupp/wiki/Hoja_de_ruta11.2 Información administrativaGrupo de discusiónDonde realizar consultas, comentarios, compartir experiencias, código, recursos, etc.http://groups.google.com/group/yuppframeworkphpBlogDonde se realizan anuncios y se publican temas interesantes relacionados con el framework y eldesarrollo web en general.http://yuppframework.blogspot.com/TwitterDesde el escritorio del framework pueden ser accedidos los twitts relacionados con este, en generalson para hacer anuncios importantes con respecto al framework.http://twitter.com/ppazosSitio del proyectoDonde publicamos las liberaciones, las notas de las versiones, preguntas frecuentes. También aquíestán los reportes de bugs y el repositorio de código (SVN).http://code.google.com/p/yupp/Documentación del proyectoDocumentación de referencia para el programador.http://www.simplewebportal.net/yupp_framework_php_doc

×