]> Guía de Referencia del Programador Zend Framework Guía de Referencia del Programador para Zend Framework 2005- Zend Technologies Inc. () Introducción a Zend Framework Descripción general Zend Framework (ZF) es un framework de código abierto para desarrollar aplicaciones web y servicios web con PHP5. ZF es una implementación que usa código 100% orientado a objetos. La estructura de los componentes de ZF es algo único; cada componente está construido con una baja dependencia de otros componentes. Esta arquitectura débilmente acoplada permite a los desarrolladores utilizar los componentes por separado. A menudo se refiere a este tipo de diseño como "use-at-will" (uso a voluntad). Aunque se pueden utilizar de forma individual, los componentes de la librería estándar de Zend Framework conforman un potente y extensible framework de aplicaciones web al combinarse. ZF ofrece un gran rendimiento y una robusta implementación MVC, una abstración de base de datos fácil de usar, y un componente de formularios que implementa la prestación de formularios HTML, validación y filtado para que los desarrolladores puedan consolidar todas las operaciones usando de una manera sencilla la interfaz orientada a objetos. Otros componentes, como Zend_Auth y Zend_Acl, proveen autentificación de usuarios y autorización diferentes a las tiendas de certificados comunes (REVISAR ESTO). También existen componentes que implementan librerías de cliente para acceder de forma sencilla a los web services más populares. Cualesquiera que sean las necesidades de su solicitud, usted tiene todas las posibilidades de encontrar un componente de Zend Framework que se pueda utilizar para reducir drásticamente el tiempo de desarrollo, con una base completamente sólida. El principal patrocinador del proyecto Zend Framework es Zend Technologies, pero muchas empresas han contribuido con componentes o características importantes para el marco. Empresas como Google, Microsoft y StrikeIron se han asociado con Zend para proporcionar interfaces de servicios web y otras tecnologías que desean poner a disposición de los desarrolladores de Zend Framework. Zend Framework no podría haber proporcionado y apoyado todas estas características sin la ayuda de la vibrante comunidad de ZF. Los miembros de la comunidad, incluidos los contribuyentes, están disponibles en las , listas de correo, canales de IRC, y en otros foros. Cualquier duda que tenga acerca de ZF, la comunidad está siempre disponible para responder. Instalación Zend Framework requiere por lo menos PHP 5.1.4 o superior, aunque Zend recomienda encarecidamente la versión 5.2.3 o superior, porque hay parches de seguridad y mejoras en el rendimiento entre estas dos versiones. Por favor, consulte el anexo sobre los requisitos del sistema. para obtener más información. La instalación del Zend Framework es muy simple. Una vez que haya descargado y descomprimido el framework, deberá añadir la carpeta "/library" de la distribución al principio de su "include path". También puede mover la carpeta "library" a cualquier otra posición (compartida o no) de su sistema de archivos. Descargar la última versión estable. Esta versión esta disponible en formatos.zip. .tar.gz, es una buena opción para aquellos que comienzan o son nuevos en Zend Framework. Download the latest nightly snapshot. For those who would brave the cutting edge, the nightly snapshots represent the latest progress of Zend Framework development. Snapshots are bundled with documentation either in English only or in all available languages. If you anticipate working with the latest Zend Framework developments, consider using a Subversion (SVN) client. Using a Subversion (SVN) client. Zend Framework is open source software, and the Subversion repository used for its development is publicly available. Consider using SVN to get the Zend Framework if you already use SVN for your application development, want to contribute back to the framework, or need to upgrade your framework version more often than releases occur. Exporting is useful if you want to get a particular framework revision without the .svn directories as created in a working copy. Checking out a working copy is good when you might contribute to Zend Framework, and a working copy can be updated any time with svn update. An externals definition is highly convenient for developers already using SVN to manage their application working copies. La URL del almacén del repository SVN de Zend Framework es: http://framework.zend.com/svn/framework/standard/trunk Una vez que tenga disponible una copia de Zend Framework, su aplicación necesita poder acceder a las clases del framework. Aunque hay diferentes maneras de lograr esto, su include_path de PHP necesita contener una ruta a la librería de Zend Framework. Zend provides a QuickStart to get you up and running as quickly as possible. This is an excellent way to begin learning about the framework with an emphasis on real world examples that you can built upon. Ya que los componentes de Zend Framework están débilmente conectados, tiene la opción de usar cualquier combinación de ellos en sus aplicaciones. Los siguientes capítulos presentan una referencia exhaustiva de Zend Framework, componente a componente. Zend_Acl Introducción Zend_Acl provee la implementación de un sistema simple y flexible de Listas de Control de Acceso (ACL, por sus siglas en inglés) para la administración de privilegios. En general, una aplicación puede utilizar las ACL para controlar el acceso a ciertos objetos protegidos, que son requeridos por otros objetos. Para los propósitos de esta documentación, Un recurso es un objeto al cual el acceso esta controlado. Un rol es un objeto que puede solicitar acceso a un recurso. En términos generales, Los roles solicitan acceso a los recursos . Por ejemplo, si una persona solicita acceso a un automóvil, entonces la persona se convierte en el rol solicitante, y el automóvil en el recurso, puesto que el acceso al automóvil puede no estar disponible a cualquiera. A través de la especificación y uso de Listas de Control de Acceso (ACL), una aplicación puede controlar cómo los objetos solicitantes (roles) han obtenido acceso a objetos protegidos (recursos). Acerca de los Recursos En Zend_Acl, crear un recurso es muy sencillo. Zend_Acl proporciona el Zend_Acl_Resource_Interface para facilitar a los desarrolladores la creacción de recursos. Una clase solo necesita implementar su interfaz, la cual consiste en un método único, getResourceId() , para que Zend_Acl considere el objeto como un recurso. Adicionalmente, Zend_Acl_Resource se incluye con Zend_Acl como una implementación principal de recursos para los desarrolladores para extenderla hasta donde lo deseen. Zend_Acl provee un estructura de árbol a la cual pueden ser agregados múltiples recursos (o "Áreas con Controles de Acceso").Ya que los recursos son almacenados en esta estructura de árbol, estos pueden ser organizados desde lo general (hacia la raíz del árbol) a lo específico (hacia las ramas del árbol). Consultas sobre un recurso específico buscarán automáticamente, en la jerarquía del recurso, reglas asignadas a recursos anteriores a los que el recurso actual haga referencia, permitiendo la herencia simple de reglas. Por ejemplo, si una regla por defecto se aplica a cada edificio en una ciudad, uno simplemente podría asignar la regla a la ciudad, en lugar de asignar la misma regla a cada edificio. Algunos edificios pueden necesitar excepciones a la regla, sin embargo, y esto es fácil de hacer en Zend_Acl asignando esta excepción a cada edificio que necesite una excepción a la regla. Un recurso sólo puede heredar de un recurso padre, aunque este recurso padre puede tener a la vez su propio recurso padre, y así; sucesivamente. Zend_Acl también soporta privilegios sobre recursos (ej. "crear","leer","actualizar", "borrar"), y el desarrollador puede asignar reglas que afecten o a todos los privilegios o a privilegios específicos sobre un recurso. Acerca de las Reglas Al igual que los recursos, la creación de un rol también es muy simple. Zend_Acl proporciona Zend_Acl_Role_Interface para facilitar a los desarrolladores la creación de roles. Una clase solo necesita la implementación de su interfaz, la cual consiste en un método único, getRoleId() , para que Zend_Acl considere que el objeto es un Rol. Adicionalmente, Zend_Acl_Role está incluido con Zend_Acl como una implementación principal del rol para que los desarrolladores la extiendan hasta donde lo deseen. En Zend_Acl, un Rol puede heredar de otro o más roles. Esto es para soportar herencia de reglas entre roles. Por ejemplo, un Rol de usuario, como "sally", puede estar bajo uno o más roles padre, como "editor" y "administrador". El desarrollador puede asignar reglas a "editor" y "administrador" por separado, y "sally" puede heredar tales reglas de ambos, sin tener que asignar reglas directamente a "sally". Dado que la habilidad de herencia desde múltiples roles es muy util, múltiples herencias tambien introduce cierto grado de complejidad. El siguiente ejemplo ilustra la condición de ambiguedad y como Zend_Acl soluciona esto. Herencia Multiple entre Roles El siguiente código define tres roles principales - " invitado ", " miembro ", y " admin " - de los cuales otros roles pueden heredar. Entonces, un rol identificado como " unUsuario " es colocado y hereda de los otros tres roles. El orden en el cual estos roles aparecen en el array $parents es importante. Cuando es necesario, Zend_Acl busca por reglas de acceso definidas no solo para el rol solicitado (aquí, " unUsuario "), sino también sobre los roles heredados (aquí, " invitado ", " miembro ", y " admin "): addRole(new Zend_Acl_Role('invitado')) ->addRole(new Zend_Acl_Role('miembro')) ->addRole(new Zend_Acl_Role('admin')); $parents = array('invitado', 'miembro', 'admin'); $acl->addRole(new Zend_Acl_Role('unUsuario'), $parents); require_once 'Zend/Acl/Resource.php'; $acl->add(new Zend_Acl_Resource('unRecurso')); $acl->deny('invitado', 'unRecurso'); $acl->allow('miembro', 'unRecurso'); echo $acl->isAllowed('unUsuario', 'unRecurso') ? 'permitido' : 'denegado';]]> Ya que no hay reglas específicamente definidas para el rol " unUsuario " y " unRecurso ", Zend_Acl debe buscar por reglas que puedan estar definidas para roles " unUsuario " hereda. Primero, el rol " admin " es visitado, y no hay regla de acceso definida para éste. Luego, el rol " miembro " es visitado, y Zend_Acl encuentra que aquí hay una regla especificando que " miembro " tiene permiso para acceder a " unRecurso ". Así, Zend_Acl va a seguir examinando las reglas definidas para otros roles padre, sin embargo, encontraría que " invitado " tiene el acceso denegado a " unRecurso ". Este hecho introduce una ambigüedad debido a que ahora " unUsuario " está tanto denegado como permitido para acceder a " unRecurso ", por la razon de tener un conflicto de reglas heredadas de diferentes roles padre. Zend_Acl resuelve esta ambiguedad completando la consulta cuando encuentra la primera regla que es directamente aplicable a la consulta. En este caso, dado que el rol " miembro " es examinado antes que el rol " invitado ", el código de ejemplo mostraría " permitido ". Cuando se especifican múltiples padres para un Rol, se debe tener en cuenta que el último padre listado es el primero en ser buscado por reglas aplicables para una solicitud de autorización. Creando las Listas de Control de Acceso (ACL) Una ACL puede representar cualquier grupo de objetos físicos o virtuales que desee. Para propósitos de demostración, sin embargo, crearemos un ACL básico para un Sistema de Administración de Contenido que mantendrá varias escalas de grupos sobre una amplia variedad de áreas. Para crear un nuevo objeto ACL, iniciamos la ACL sin parámetros: Hasta que un desarrollador especifique una regla "permitido", Zend_Acl deniega el acceso a cada privilegio sobre cada recurso para cada rol. Registrando Roles El Sistema de Administración de Contenido casi siempre necesita una jerarquía de permisos para determinar la capacidad de identificación de sus usuarios. Puede haber un grupo de 'Invitados' para permitir acceso limitado para demostraciones, un grupo de 'Personal' para la mayoría de usuarios del CMS quienes realizan la mayor parte de operaciones del día a día, un grupo 'Editores' para las responsabilidades de publicación, revisión, archivo y eliminación de contenido, y finalmente un grupo 'Administradores' cuyas tareas pueden incluir todas las de los otros grupos y también el mantenimiento de la información delicada, manejo de usuarios, configuración de los datos básicos y su respaldo/exportación. Este grupo de permisos pueden ser representados en un registro de roles, permitiendo a cada grupo heredar los privilegios de los grupos 'padre', al igual que proporcionando distintos privilegios solo para su grupo individual. Los permisos pueden ser expresados como: Controles de Acceso para un CMS de ejemplo Nombre Permisos Individuales Hereda permisos de Invitado View N/A Personal Editar, Enviar, Revisar Invitado Editor Publicar, Archivar, Eliminar Personal Administrador (Todos los accesos permitidos) N/A
Para este ejemplo, se usa Zend_Acl_Role , pero cualquier objeto que implemente Zend_Acl_Role_Interface es admisible. Estos grupos pueden ser agragados al registro de roles de la siguiente manera: addRole($rolInvitado); // Personal hereda de Invitado $acl->addRole(new Zend_Acl_Role('personal'), $rolInvitado); /* alternativamente, lo de arriba puede ser escrito así: $rolInvitado = $acl->addRole(new Zend_Acl_Role('personal'), 'invitado'); //*/ // Editor hereda desde personal $acl->addRole(new Zend_Acl_Role('editor'), 'personal'); // Administrador no hereda controles de acceso $acl->addRole(new Zend_Acl_Role('administrador'));]]>
Definiendo Controles de Acceso Ahora que la ACL contiene los roles relevantes, se pueden establecer reglas que definan cómo los roles pueden acceder a los recursos. Tenga en cuenta que no definiremos ningún recurso en particular para este ejemplo, el cual está simplificado para ilustrar que las reglas se aplican a todos los recursos. Zend_Acl proporciona una forma práctica por la cual las reglas solo necesitan ser asignadas de lo general a lo especifico, minimizando el número de reglas necesarias, porque los recursos y roles heredan reglas que están definidas en sus padres. Consecuentemente, podemos definir un grupo razonablemente complejo de reglas con un mínimo de código. Para aplicar estos permisos básicos como están definidos arriba: addRole($rolInvitado); $acl->addRole(new Zend_Acl_Role('personal'), $rolInvitado); $acl->addRole(new Zend_Acl_Role('editor'), 'personal'); $acl->addRole(new Zend_Acl_Role('administrador')); // Invitado solo puede ver el contenido $acl->allow($rolInvitado, null, 'ver'); /* Lo de arriba puede ser escrito de la siguiente forma alternativa: $acl->allow('invitado', null, 'ver'); //*/ // Personal hereda el privilegio de ver de invitado, pero también necesita privilegios adicionales $acl->allow('personal', null, array('editar', 'enviar', 'revisar')); // Editor hereda los privilegios de ver, editar, enviar, y revisar de personal, // pero también necesita privilegios adicionales $acl->allow('editor', null, array('publicar', 'archivar', 'eliminar')); // Administrador no hereda nada, pero tiene todos los privilegios permitidos $acl->allow('administrador');]]> El valor null en las llamadas de allow() es usado para indicar que las reglas de permiso se aplican a todos los recursos. Consultando la ACL Ahora tenemos una ACL flexible que puede ser usada para determinar qué solicitantes tienen permisos para realizar funciones a través de la aplicacion web. Ejecutar consultas es la forma más simple de usar el método isAllowed() : isAllowed('invitado', null, 'ver') ? "permitido" : "denegado"; // permitido echo $acl->isAllowed('personal', null, 'publicar') ? "permitido" : "denegado"; // denegado echo $acl->isAllowed('personal', null, 'revisar') ? "permitido" : "denegado"; // permitido echo $acl->isAllowed('editor', null, 'ver') ? "permitido" : "denegado"; // permitido debido a la herencia de invitado echo $acl->isAllowed('editor', null, 'actualizar') ? "permitido" : "denegado"; // denegado debido a que no hay regla de permiso para 'actualizar' echo $acl->isAllowed('administrador', null, 'ver') ? "permitido" : "denegado"; // permitido porque administrador tiene permitidos todos los privilegios echo $acl->isAllowed('administrador') ? "permitido" : "denegado"; // permitido porque administrador tiene permitidos todos los privilegios echo $acl->isAllowed('administrador', null, 'actualizar') ? "permitido" : "denegado"; // permitido porque administrador tiene permitidos todos los privilegios]]>
Perfeccionamiento de los controles de acceso Definir mejor los controles de acceso El ACL básico según lo definido en la sección anterior demuestra cómo los diversos privilegios se pueden otorgar sobre todo el ACL (todos los recursos). En la práctica, sin embargo, los controles de acceso tienden a tener excepciones y diversos grados de complejidad. Zend_Acl permite lograr estos refinamientos de una manera sencilla y flexible. Para el CMS del ejemplo se ha determinado que, si bien el grupo 'staff' cubre las necesidades de la gran mayoría de usuarios, hay una necesidad de un nuevo grupo 'marketing' que requiere el acceso al boletín de noticias y las últimas noticias en el CMS. El grupo es bastante autosuficiente y tendrá la capacidad de publicar y de archivar los boletines de noticias y las últimas noticias. Primero revisamos el registro del rol para reflejar estos cambios. Hemos determinado que el grupo 'marketing' tiene los mismos permisos básicos que 'staff', así que definimos 'marketing' de tal manera que herede los permisos de 'staff': addRole(new Zend_Acl_Role('marketing'), 'staff'); ]]> A continuación, la nota que por encima de los controles de acceso se refieren a recursos específicos (por ejemplo, "boletín informativo", "últimas noticias", "anuncio de noticias"). Ahora añadimos estos recursos: add(new Zend_Acl_Resource('newsletter')); // news $acl->add(new Zend_Acl_Resource('news')); // Últimas Noticias $acl->add(new Zend_Acl_Resource('latest'), 'news'); // anuncio de noticias $acl->add(new Zend_Acl_Resource('announcement'), 'news'); ]]> Entonces es simplemente una cuestión de la definición de estas normas más específicas en ámbitos de la ACL: allow('marketing', array('newsletter', 'latest'), array('publish', 'archive')); // Staff (y marketing, por herencia), se le denega el permiso a // revisar las últimas noticias $acl->deny('staff', 'latest', 'revise'); // Todos (incluyendo los administradores) tienen permiso denegado para // archivar anuncios y noticias $acl->deny(null, 'announcement', 'archive'); ]]> Ahora podemos consultar el ACL con respecto a los últimos cambios: isAllowed('staff', 'newsletter', 'publish') ? "allowed" : "denied"; // denegado echo $acl->isAllowed('marketing', 'newsletter', 'publish') ? "allowed" : "denied"; // permitido echo $acl->isAllowed('staff', 'latest', 'publish') ? "allowed" : "denied"; // denegado echo $acl->isAllowed('marketing', 'latest', 'publish') ? "allowed" : "denied"; // permitido echo $acl->isAllowed('marketing', 'latest', 'archive') ? "allowed" : "denied"; // permitido echo $acl->isAllowed('marketing', 'latest', 'revise') ? "allowed" : "denied"; // denegado echo $acl->isAllowed('editor', 'announcement', 'archive') ? "allowed" : "denied"; // denegado echo $acl->isAllowed('administrator', 'announcement', 'archive') ? "allowed" : "denied"; // denegado ]]> Eliminar los controles de acceso Para eliminar una o más reglas ACL, simplemente utilice el método removeAllow() o removeDeny(). Al igual que con allow() y deny(), puede utilizar un valor null para indicar que el método es aplicable a todos los roles, recursos y/o privilegios: removeDeny('staff', 'latest', 'revise'); echo $acl->isAllowed('marketing', 'latest', 'revise') ? "allowed" : "denied"; // permitido // Elimina la autorización para publicar y archivar los boletines // marketing $acl->removeAllow('marketing', 'newsletter', array('publish', 'archive')); echo $acl->isAllowed('marketing', 'newsletter', 'publish') ? "allowed" : "denied"; // denegado echo $acl->isAllowed('marketing', 'newsletter', 'archive') ? "allowed" : "denied"; // denegado ]]> Los privilegios pueden ser modificados de manera incremental como se ha indicado anteriormente, pero un valor null para los privilegios anula tales cambios incrementales: allow('marketing', 'latest'); echo $acl->isAllowed('marketing', 'latest', 'publish') ? "allowed" : "denied"; //permitido echo $acl->isAllowed('marketing', 'latest', 'archive') ? "allowed" : "denied"; //permitido echo $acl->isAllowed('marketing', 'latest', 'anything') ? "allowed" : "denied"; // permitido ]]> Uso Avanzado Almacenamiento Permanente de los Datos ACL Zend_Acl fue diseñado de tal manera que no requiere ninguna tecnología particular como bases de datos o un servidor de cache para el almacenamiento de datos ACL. Al poseer una implementación completamente construida en PHP, es posible contruir herramientas de administración personalizadas sobre Zend_Acl con relativa facilidad y flexibilidad. En muchas situaciones se requiere alguna forma de mantenimiento interactivo de una ACL, y Zend_Acl provee métodos para configurar, y consultar, los controles de acceso de una aplicación. El almacenamiento de los datos ACL es una tarea que se delega al desarrollador, puesto que la utilización variará exténsamente en distintas situaciones. Dado que Zend_Acl es serializable, los objetos ACL pueden serializarse con la función serialize() de PHP, y los resultados pueden ser almacenados donde sea que el desarrollador lo desee, en un archivo, base de datos, o mecanismo de cache Escribiendo reglas condicionales ACL con aserciones A veces, una regla para permitir o negar una función de acceso a un recurso no debería ser absoluta sino que depende de varios criterios. Por ejemplo, supóngase que debe permitirse cierto acceso, pero únicamente entre las 8:00am y 5:00pm. Otro ejemplo sería negar el acceso debido a una petición que proviene de una dirección IP que se ha marcado como una fuente de abusos. Zend_Acl tiene soporte para la aplicación de normas basadas en cualquier condición que el desarrollador necesite. Zend_Acl provee soporte para reglas condicionales con Zend_Acl_Assert_Interface . Con el fin de utilizar la regla de aserción de la interfaz, un desarrollador escribe una clase que implemente el método assert() de la interfaz: _isCleanIP($_SERVER['REMOTE_ADDR']); } protected function _isCleanIP($ip) { // ... } } ]]> Una vez la clase de aserción esta disponible, el desarrollador puede suministrar una instancia de la clase de aserción cuando asigna reglas condicionales. Una regla que es creada con una aserción sólo se aplica cuando el método de la aserción devuelve true. allow(null, null, null, new CleanIPAssertion()); ]]> El código anterior crea una regla condicional que permite el acceso a todos los privilegios sobre todo, por todo el mundo, excepto cuando la IP de quien hace la petición está en la "lista negra". Si una petición viene desde una IP que no está considerada "limpia", entonces la regla no se aplica. Dado que la regla se aplica a todos los roles, todos los recursos, y todos los privilegios, una IP "no limpia" daría lugar a una negación de acceso. Éste es un caso especial, sin embargo, y debería ser entendido que en todos los otros casos (por ejemplo, cuando un rol específico, recurso, o privilegio está especificado por la regla), una aserción fallida provoca que la regla no se aplique, y otras reglas deberían ser usadas para determinar si el acceso está permitido o denegado. El método assert() de un objeto aserción es pasado a la ACL, regla, recurso, y privilegio para el cual una consulta de autorización (por ejemplo, isAllowed() ) se aplica, con el fin de proporcionar un contexto para que la clase de aserción determine sus condiciones cuando fuera necesario.
Zend_Amf IntroducciГіn Zend_Amf provee apoyo para el Formato de Mensajes de ActionScript Action Message Format (AMF) de Adobe, que permite la comunicaciГіn entre Adobe Flash Player y PHP. EspecГ­ficamente, proporciona una aplicaciГіn para un servidor gateway que tramita las solicitudes enviadas desde Flash Player al servidor, mapeando estos requerimientos al objeto y a sus mГ©todos de clase, como asГ­ tambiГ©n a llamadas arbitrarias de comunicaciГіn. Las url="http://download.macromedia.com/pub/labs/amf/amf3_spec_121207.pdf"> especificaciones (en inglГ©s) de AMF3 son de libre disponibilidad y sirven como referencia para establecer quГ© tipos de mensajes pueden ser enviados entre Flash Player y el servidor. Zend_Amf_Server Zend_Amf_Server proporciona un servidor al estilo RPC para tramitar solicitudes hechas desde Adobe Flash Player utilizando el protocolo AMF. Al igual que todas las clases de servidor, Zend Framework sigue la API de SoapServer, proporcionando una interfaz para crear servidores fácil de recordar. Servidor AMF básico Asumamos que ha creado la clase Foo con una variedad de métodos públicos. Usando el siguiente código, puede crear un servidor AMF: setClass('Foo'); $respuesta = $servidor->handle(); echo $respuesta; ]]> Alternativamente, en su lugar puede elegir agregar una función simple como llamada de retorno: addFunction('myUberCoolFunction'); $respuesta = $servidor->handle(); echo $respuesta; ]]> También puede combinar y examinar la identidad de varias clases y funciones. Al hacerlo, sugerimos darle un espacio de nombres a cada una para garantizar que no ocurran colisiones entre nombres de métodos; puede hacerse simplemente pasando una segunda cadena de argumentos para cualquier addFunction() o setClass(): addFunction('myUberCoolFunction', 'my') ->setClass('Foo', 'foo') ->setClass('Bar', 'bar'); $respuesta = $servidor->handle(); echo $respuesta; ]]> El Zend_Amf_Server también permite cargar servicios dinámicamente, en función de una ruta de directorio ya suministrada. Puede añadir al servidor tantos directorios como desee. El orden en que se añadan los directorios al servidor será el orden en que se realizarán las búsquedas LIFO en los directorios para coincidir con la clase. El método addDirectory() realiza la acción de añadir directorios. addDirectory(dirname(__FILE__) .'/../services/'); $servidor->addDirectory(dirname(__FILE__) .'/../package/'); ]]> Cuando se llama a servicios remotos, los nombres de los directorios que contengan las fuentes pueden tener los delimitadores guión bajo (_) y el punto (.). Cuando se utilize un guión bajo (_) tanto en PEAR como en Zend Framework, se respetarán los nombres de clases de acuerdo a las convenciones de nomenclatura. Esto significa que si usted llama al servicio com_Foo_Bar el servidor buscará el archivo Bar.php en cada una de las rutas incluidas en com/Foo/Bar.php. Si se usa la notación punto para su servicio remoto como com.Foo.Bar cada ruta incluida deberá tener com/Foo/Bar.php agregado al final para autocargar Bar.php. Todos las solicitudes AMF enviadas al script serán manejadas por el servidor, y este devolverá una respuesta AMF. Todos los métodos y las funciones agregadas requieren bloques de documentación (docblocks) Como todos los demás componentes del servidor en Zend Framework, debe documentar los métodos de su clase usando PHP docblocks. Como mínimo, necesita proporcionar anotaciones para cada argumento así como para el valor de retorno. Como ejemplos: Pueden usarse otras anotaciones, pero serán ignoradas. Conectándose al Servidor desde Flex Conectarse a Zend_Amf_Server desde su proyecto Flex es bastante simple; solo necesita apuntar el final del URI a su script Zend_Amf_Server. Por ejemplo, digamos que usted ya ha creado su servidor y lo ha puesto en el fichero server.php en el directorio raíz (root) de su aplicación, por lo tanto la URI es http://example.com/server.php. En este caso, usted debería modificar su fichero services-config.xml poniendo este valor como atributo al punto final del canal uri. Si nunca ha creado un fichero services-config.xml puede hacerlo abriendo su proyecto en la ventana del navegador. Haga clic derecho sobre el nombre del proyecto y seleccione 'properties' (propiedades). En el cuadro de diálogo 'properties' del proyecto ir al menú ‘Flex Build Path' (Crear ruta Flex), luego en la pestaña ‘Library path’ (ruta de biblioteca) asegúrese de que el fichero 'rpc.swc' sea añadido a su ruta de proyectos y pulse Ok (Aceptar) para cerrar la ventana. También necesitará indicarle al compilador que debe usar services-config.xml para encontrar el punto final de RemoteObject. Para hacerlo, abra de nuevo el panel de propiedades de su proyecto haciendo clic en el botón derecho sobre el proyecto en la carpeta del navegador y seleccione 'properties' (propiedades). Ahora seleccione ‘Flex Compiler' (Compilador Flex) y añada la cadena: -services “services-config.xml". Presione 'Apply' (Aplicar) y luego en OK para volver a actualizar la opción. Lo que acaba de hacer es decirle al compilador Flex que busque en el fichero services-config.xml aquellas variables que se usarán en tiempo de ejecución por la clase RemotingObject. Ahora, para conectarnos a nuestros métodos remotos debemos indicarle a Flex qué fichero de configuración de servicios utilizar. Por esta razón creamos un nuevo fichero 'services-config.xml' en la carpeta src del proyecto Flex. Con click derecho sobre el proyecto y seleccionando 'new'(nuevo) 'File' (fichero), se abrirá una nueva ventana. Seleccione la carpeta del proyecto y luego nombre el archivo 'services-config.xml' y presione 'finish' (finalizar). Flex ha creado y abierto el nuevo fichero services-config.xml. Utilice el siguiente texto de ejemplo para su fichero services-config.xml. Asegúrese de actualizar su punto final para que concuerde con el servidor. Asegúrese también de guardar el fichero. * ]]> Hay dos puntos clave en el ejemplo. En primer lugar, pero último en el listado, creamos un canal AMF, y especificamos el punto final como la URL a nuestro Zend_Amf_Server: ]]> Advierta que a este canal le hemos dado un identificador, "zend-endpoint". El ejemplo crea un servicio cuyo destino hace referencia a este canal, asignándole también un ID, en este caso es "zend". Dentro de nuestros ficheros Flex MXML, necesitamos vincular un RemoteObject al servicio. En MXML, esto podría hacerse así: ]]> Aquí, hemos definido un nuevo objeto remoto identificado por "myservice" vinculado destino de servicio "zend" que hemos definido en el fichero services-config.xml. Entonces invocamos sus métodos en nuestro ActionScript simplemente llamando a "myservice.<method>". (En este caso, el código original "RoundTrip" es el nombre de nuestra aplicación Flex). Un ejemplo: Cuando se usan nombres-de-espacio, puede usarse "myservice.<namespace>.<method>": Para más información sobre como invocar a Flex RemoteObject visite el sitio de ayuda de Adobe Flex 3 en:. Manejo de errores Por defecto, todas las excepciones producidas en sus clases o funciones adjuntas serán capturados y devueltas como mensajes de error de AMF (AMF ErrorMessages). Sin embargo, el contenido de estos objetos de mensajes de error variará dependiendo de si el servidor está o no en modo "producción" (el estado por defecto). Cuando se está en modo de producción, únicamente el código de excepción será devuelto. Si desactiva el modo de producción, algo que debe hacerse sólo para probar -- serán devueltos más detalles de la excepción: el mensaje de excepción (error), línea y backtrace serán adjuntados. Para desactivar el modo de producción, haga lo siguiente: setProduction(false); ]]> Para habilitarlo nuevamente, pase el valor true en su lugar. setProduction(true); ]]> ¡Deshabilite el modo de producción racionalmente! Sugerimos deshabilitar el modo de producción solo cuando se está en modo de desarrollo. Los mensajes de excepción y los backtraces puede contener información sensible del sistema, y no desea que se pueda acceder a ellas desde el exterior. Aunque AMF es un formato binario, ahora al ser abierta la especificación, cualquiera puede potencialmente deserializar los datos. Un área en la que se debe tener especialmente mucho cuidado son los errores propios de PHP. Cuando la directiva INI display_errors está habilitada, los errores de PHP de cualquier nivel del reporte actual serán pasados directamente a la salida, y potencialmente se podrían hacer estragos con las respuestas de AMF. Para prevenir estos problemas, sugerimos deshabilitar la directiva display_errors cuando se está en modo de producción. Respuestas de AMF En ocasiones es posible que quiera manipular ligeramente el objeto respuesta, es bastante usual querer devolver algunas cebeceras de mensajes adicionales. Puede hacerlo mediante el método del servidor handle() que devuelve el objeto respuesta. Agregar cabeceras de mensaje a la respuesta de AMF En este ejemplo, añadiremos la cabecera de mensaje (MessageHeader) "foo" con el valor 'bar' a la respuesta antes de devolverla. handle(); $respuesta->addAmfHeader(new Zend_Amf_Value_MessageHeader('foo', true, 'bar')) echo $respuesta; ]]> Objetos tipados Similarmente a SOAP, AMF permite pasar objetos entre cliente y servidor. Esto le da una gran flexibilidad y coherencia a ambos entornos. Zend_Amf ofrece tres métodos para mapear ActionScript y objetos PHP. Primero, usted puede crear uniones explícitas a nivel del servidor, utilizando el método setClassMap(). El primer argumento es el nombre de la clase de ActionScript, el segundo es el nombre de la clase PHP que lo mapea: setClassMap('ContactVO', 'Contact'); ]]> Segundo, en su clase PHP puede ajustar la propiedad como pública mediante $_explicitType, con el valor representativo de la clase ActionScript que mapear: Tercero, en un sentido similar, puede definir como público el método getASClassName() dentro de su clase. Este método debe devolver la clase ActionScript apropiada: Aunque hemos creado ContactVO en el servidor, ahora tenemos que hacer su clase correspondiente en AS3 para que el servidor pueda mapear el objeto. Haga clic derecho sobre la carpeta src del proyecto Flex y seleccione New -> ActionScript File. Nombre el fichero como ContactVO y pulse 'finish' (finalizar) para verlo. Copie el siguiente código en el fichero para terminar de crear la clase. La clase es sintácticamente equivalente a la de PHP del mismo nombre. Los nombres de variables son exactamente los mismos y necesitan estar en el mismo contenedor para trabajar correctamente. Hay dos meta tags AS3 únicos en esta clase. El primero es vinculable y dispara un evento cuando es actualizada. El segundo es el tag RemoteClass y define que esta clase puede tener mapeado un objeto remoto con un nombre de alias, en este caso ContactVO Es obligatorio que en esta etiqueta(tag), el valor que se estableció es la clase PHP sea estrictamente equivalente. El siguiente resultado del evento debido a la llamada de servicio, se incorporó instantáneamente a ContactVO de Flex. Cualquier cosa que esté ligada a myContact será actualizada con los datos retornados por ContactVO. Conectándose al servidor desde Flash La conexión a Zend_Amf_Server desde su proyecto Flash es ligeramente distinta a la de Flex. Sin embargo una vez que la conexión con Flash funcione con Zend_Amf_Server lo hará igual modo que con Flex. El siguiente ejemplo también puede ser utilizado desde un fichero Flex AS3. Para nuestra conexión vamos a reutilizar la misma configuracion Zend_Amf_Server junto a la clase Mundo. Abra Flash CS y cree un nuevo fichero Flash (ActionScript 3). Nombre al documento como ZendExample.fla y guárdelo en una carpeta que utilizará para este ejemplo. Cree una nuevo fichero AS3 en el mismo directorio y llámelo Main.as. Abra ambos ficheros con su editor. Ahora vamos a conectar las dos ficheros a través de la clase documento. Seleccione ZendExample y haga clic en el escenario. Desde el panel del escenario cambie la propiedad de la clase Document a Main. Esto vincula al fichero Main.as con la interfaz de usuario en ZendExample.fla. Cuando ejecute el fichero ZendExample de Flash se ejecutará ahora la clase Main.as. El paso siguiente será añadir ActionScript para hacer una lamada AMF. Ahora vamos a hacer una clase Main(principal) para que podamos enviar los datos al servidor y mostrar el resultado. Copie el código siguiente en su fichero Main.as y luego vamos a recorrer el código para describir cuál es el papel de cada elemento. Primero tenemos que importar dos bibliotecas de ActionScript que realizan la mayor parte del trabajo. La primera es NetConnection que actúa como un tubo bidireccional entre el cliente y el servidor. La segunda es un objeto Responder que maneja los valores de retorno desde el servidor, y que están relacionados con el éxito o el fracaso de la llamada. En la clase necesitaremos tres variables para representar a NetConnection, Responder, y la URL del gateway a nuestra instalación Zend_Amf_Server. En el constructor Main creamos un Responder(respondedor) y una nueva conexión al punto final de Zend_Amf_Server. El respondedor define dos diferentes métodos para manejar la respuesta desde el servidor. Por simplicidad los hemos llamado onResult y onFault. La función onComplete se ejecuta tan pronto como la construcción ha concluido, enviando los datos al servidor. Necesitamos añadir una línea más que hace una llamada a la función Zend_Amf_Server Mundo->hola. Cuando creamos la variable responder hemos definido las funciones onResult y onFault para manejar la respuesta proveniente del servidor. Hemos añadido la función OnResult para el resultado exitoso desde el servidor. Cada vez que se ejecuta apropiadamente el manejo de conexión con el servidor, el manejador de eventos llama esta función. La función onFault, se llama si hubo una respuesta nula desde el servidor. Esto ocurre cuando hay un error en el servidor, la URL al servidor es inválida, el servicio remoto o método no existe o cualquier otra cuestión relacionada con la conexión. La inclusión de ActionScript para realizar la conexión remota ha finalizado. Al ejecutar el fichero ZendExample, se establece una conexión con Zend_Amf. En resumen, se han añadido las variables requeridas para abrir una conexión con el servidor remoto, se han definido qué métodos se deben utilizar cuando su aplicación recibe una respuesta desde el servidor, y finalmente se han mostrado los datos de salida devueltos a través de trace(). Zend_Auth Introduction Zend_Auth provides an API for authentication and includes concrete authentication adapters for common use case scenarios. Zend_Auth is concerned only with authentication and not with authorization. Authentication is loosely defined as determining whether an entity actually is what it purports to be (i.e., identification), based on some set of credentials. Authorization, the process of deciding whether to allow an entity access to, or to perform operations upon, other entities is outside the scope of Zend_Auth. For more information about authorization and access control with the Zend Framework, please see Zend_Acl. The Zend_Auth class implements the Singleton pattern - only one instance of the class is available - through its static getInstance() method. This means that using the new operator and the clone keyword will not work with the Zend_Auth class; use Zend_Auth::getInstance() instead. Adapters A Zend_Auth adapter is used to authenticate against a particular type of authentication service, such as LDAP, RDBMS, or file-based storage. Different adapters are likely to have vastly different options and behaviors, but some basic things are common among authentication adapters. For example, accepting authentication credentials (including a purported identity), performing queries against the authentication service, and returning results are common to Zend_Auth adapters. Each Zend_Auth adapter class implements Zend_Auth_Adapter_Interface. This interface defines one method, authenticate(), that an adapter class must implement for performing an authentication query. Each adapter class must be prepared prior to calling authenticate(). Such adapter preparation includes setting up credentials (e.g., username and password) and defining values for adapter- specific configuration options, such as database connection settings for a database table adapter. The following is an example authentication adapter that requires a username and password to be set for authentication. Other details, such as how the authentication service is queried, have been omitted for brevity: As indicated in its docblock, authenticate() must return an instance of Zend_Auth_Result (or of a class derived from Zend_Auth_Result). If for some reason performing an authentication query is impossible, authenticate() should throw an exception that derives from Zend_Auth_Adapter_Exception. Results Zend_Auth adapters return an instance of Zend_Auth_Result with authenticate() in order to represent the results of an authentication attempt. Adapters populate the Zend_Auth_Result object upon construction, so that the following four methods provide a basic set of user-facing operations that are common to the results of Zend_Auth adapters: isValid() - returns true if and only if the result represents a successful authentication attempt getCode() - returns a Zend_Auth_Result constant identifier for determining the type of authentication failure or whether success has occurred. This may be used in situations where the developer wishes to distinguish among several authentication result types. This allows developers to maintain detailed authentication result statistics, for example. Another use of this feature is to provide specific, customized messages to users for usability reasons, though developers are encouraged to consider the risks of providing such detailed reasons to users, instead of a general authentication failure message. For more information, see the notes below. getIdentity() - returns the identity of the authentication attempt getMessages() - returns an array of messages regarding a failed authentication attempt A developer may wish to branch based on the type of authentication result in order to perform more specific operations. Some operations developers might find useful are locking accounts after too many unsuccessful password attempts, flagging an IP address after too many nonexistent identities are attempted, and providing specific, customized authentication result messages to the user. The following result codes are available: The following example illustrates how a developer may branch on the result code: _auth->authenticate($adapter); switch ($result->getCode()) { case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND: /** do stuff for nonexistent identity **/ break; case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID: /** do stuff for invalid credential **/ break; case Zend_Auth_Result::SUCCESS: /** do stuff for successful authentication **/ break; default: /** do stuff for other failure **/ break; } ]]> Identity Persistence Authenticating a request that includes authentication credentials is useful per se, but it is also important to support maintaining the authenticated identity without having to present the authentication credentials with each request. HTTP is a stateless protocol, however, and techniques such as cookies and sessions have been developed in order to facilitate maintaining state across multiple requests in server-side web applications. Default Persistence in the PHP Session By default, Zend_Auth provides persistent storage of the identity from a successful authentication attempt using the PHP session. Upon a successful authentication attempt, Zend_Auth::authenticate() stores the identity from the authentication result into persistent storage. Unless configured otherwise, Zend_Auth uses a storage class named Zend_Auth_Storage_Session, which, in turn, uses Zend_Session. A custom class may instead be used by providing an object that implements Zend_Auth_Storage_Interface to Zend_Auth::setStorage(). If automatic persistent storage of the identity is not appropriate for a particular use case, then developers may forgo using the Zend_Auth class altogether, instead using an adapter class directly. Modifying the Session Namespace Zend_Auth_Storage_Session uses a session namespace of 'Zend_Auth'. This namespace may be overridden by passing a different value to the constructor of Zend_Auth_Storage_Session, and this value is internally passed along to the constructor of Zend_Session_Namespace. This should occur before authentication is attempted, since Zend_Auth::authenticate() performs the automatic storage of the identity. setStorage(new Zend_Auth_Storage_Session('someNamespace')); /** * @todo Set up the auth adapter, $authAdapter */ // Authenticate, saving the result, and persisting the identity on // success $result = $auth->authenticate($authAdapter); ]]> Implementing Customized Storage Sometimes developers may need to use different identity persistence behavior than that provided by Zend_Auth_Storage_Session. For such cases developers may simply implement Zend_Auth_Storage_Interface and supply an instance of the class to Zend_Auth::setStorage(). Using a Custom Storage Class In order to use an identity persistence storage class other than Zend_Auth_Storage_Session, a developer implements Zend_Auth_Storage_Interface: In order to use this custom storage class, Zend_Auth::setStorage() is invoked before an authentication query is attempted: setStorage(new MyStorage()); /** * @todo Set up the auth adapter, $authAdapter */ // Authenticate, saving the result, and persisting the identity on // success $result = Zend_Auth::getInstance()->authenticate($authAdapter); ]]> Using Zend_Auth There are two provided ways to use Zend_Auth adapters: indirectly, through Zend_Auth::authenticate() directly, through the adapter's authenticate() method The following example illustrates how to use a Zend_Auth adapter indirectly, through the use of the Zend_Auth class: authenticate($authAdapter); if (!$result->isValid()) { // Authentication failed; print the reasons why foreach ($result->getMessages() as $message) { echo "$message\n"; } } else { // Authentication succeeded; the identity ($username) is stored // in the session // $result->getIdentity() === $auth->getIdentity() // $result->getIdentity() === $username } ]]> Once authentication has been attempted in a request, as in the above example, it is a simple matter to check whether a successfully authenticated identity exists: hasIdentity()) { // Identity exists; get it $identity = $auth->getIdentity(); } ]]> To remove an identity from persistent storage, simply use the clearIdentity() method. This typically would be used for implementing an application "logout" operation: clearIdentity(); ]]> When the automatic use of persistent storage is inappropriate for a particular use case, a developer may simply bypass the use of the Zend_Auth class, using an adapter class directly. Direct use of an adapter class involves configuring and preparing an adapter object and then calling its authenticate() method. Adapter-specific details are discussed in the documentation for each adapter. The following example directly utilizes MyAuthAdapter: authenticate(); if (!$result->isValid()) { // Authentication failed; print the reasons why foreach ($result->getMessages() as $message) { echo "$message\n"; } } else { // Authentication succeeded // $result->getIdentity() === $username } ]]> Zend_Cache Introducción Zend_Cache provee una forma genérica para cualquier caché de datos. El almacenamiento en caché en Zend Framework se opera por interfaces, mientras que los registros de caché son almacenados a través de adapatadores del backend ( Archivo , Sqlite , Memcache ...) mediante un sistema flexible de documentos de identidad y etiquetas. Utilizando éstas, es fácil en el futuro eliminar determinados tipos de registro.(Ejemplo: "eliminar todos los registros caché de determinada etiqueta"). El módulo principal ( Zend_Cache_Core ) es genérico, flexible y configurable. Aun para sus necesidades específicas existen frontends de caché que extienden Zend_Cache_Core a conveniencia: Output , File , Function y Class . Obtener un frontend con <code>Zend_Cache::factory()</code> Zend_Cache::factory() ejemplifica objetos correctos y los une. En este primer ejemplo, usaremos el frontend Core junto con el backend File . 7200, // tiempo de vida de caché de 2 horas 'automatic_serialization' => true ); $backendOptions = array( 'cache_dir' => './tmp/' // Carpeta donde alojar los archivos de caché ); // getting a Zend_Cache_Core object $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions); ]]> Frontends y Backends Compuestos de Múltiples Palabras Algunos frontends y backends se nombran usando varias palabras, tal como 'ZenPlatform'. Al fabricarlas las especificamos, las separamos usando un separador de palabras, como un espacio (' '), guión ('-'), o punto ('.'). Almacenando en caché un resultado de consulta a una base de datos Ahora que tenemos un frontend, podemos almacenar en caché cualquier tipo de dato (hemos activado la serialización). Por ejemplo, podemos almacenar en caché un resultado de una consulta de base de datos muy costosa. Después de ser almacenada en caché, no es necesario ni conectar la base de datos; los registros se obtienen del caché de forma no serializada. load('myresult')) { // no existe cache; conectar a la base de datos $db = Zend_Db::factory( [...] ); $result = $db->fetchAll('SELECT * FROM huge_table'); $cache->save($result, 'myresult'); } else { // cache existosa!, darlo a conocer echo "Éste es de caché!\n\n"; } print_r($result); ]]> El almacenamiento en caché de salida con la interfaz de salida <code>Zend_Cache</code> ‘Resaltamos’ las secciones en las que deseamos almacenar en caché la salida, mediante la adición de algunas condiciones lógicas, encapsulamos la sección dentro de los métodos start() y end() (esto se parece al primer ejemplo y es la estrategia fundamental para el almacenamiento en caché). Dentro, los datos de salida, como siempre – todas las salidas serán almacenadas en caché cuando se ordene la ejecución del método end() . En la siguiente ejecución, toda la sección se saltará a favor de la búsqueda de datos del caché (tanto tiempo como el registro del caché sea válido). 30, // tiempo de vida de caché de 30 segundos 'automatic_serialization' => false // éste es el valor por defecto ); $backendOptions = array('cache_dir' => './tmp/'); $cache = Zend_Cache::factory('Output', 'File', $frontendOptions, $backendOptions); // Pasamos un identificador único al método start() if(!$cache->start('mypage')) { // salida como de costumbre: echo 'Hola mundo! '; echo 'Esto está en caché ('.time().') '; $cache->end(); // la salida es guardada y enviada al navegador } echo 'Esto no estará en caché nunca ('.time().').'; ]]> Note que delineamos el resultado de time() dos veces; esto es algo dinámico para los propósitos de la demostración. Trate de ejecutarlo y entonces regenérelo muchas veces; notará que el primer número no cambia mientras que el segundo cambia a medida que pasa el tiempo. Esto es porque el primer número esta delineado en la sección caché y esta guardado en medio de otras salidas. Después de medio minuto (habremos establecido el tiempo de vida de 30 segundos) los números deben acoplarse nuevamente porque el registro caché ha expirado -- sólo para ser almacenado en caché nuevamente. Deberá probarlo en su visualizador o consola. Cuando usamos Zend_Cache, ponemos atención a la importación del identificador caché (pasado a save() y start() ). Éste deberá ser único para cada recurso que se almacene en caché, de otra manera los registros almacenados en caché que no se vinculan podrían borrarse unos a otros, o peor aún, mostrarse uno en lugar del otro. Zend_Captcha Introducción CAPTCHA es el acrónimo de "Completely Automated Public Turing test to tell Computers and Humans Apart" (Prueba de Turing pública y automática para diferenciar a máquinas y humanos). Es usado como un desafío-respuesta para asegurar que la información individual suministrada viene de un humano y no de un proceso automatizado. Típicamente, un captcha es usado con envío de formularios donde no es necesario que el usuario se haya autenticado, pero se desea prevenir el envío de spam. Los Captchas pueden presentarse en multitud de formas, incluyendo preguntas lógicas, caracteres trastocados o presentar imágenes y preguntar cómo se relacionan. Zend_Captcha intenta proveer una amalgama de backends que pueden ser utilizados por separado o en conjunción con Zend_Form . Zend_Config Introducción Zend_Config está diseñado para simplificar el acceso y el uso de datos de configuración dentro de aplicaciones. Provee una interfaz de usuario basada en propiedades de objetos anidadas para acceder a datos de configuración dentro del código de la aplicación. Los datos de configuración pueden venir de multitud de medios que soporten almacenamiento de datos de forma jerárquica. Actualmente Zend_Config provee adaptadores para datos de configuración que están almacenados en archivos de texto con Zend_Config_Ini y Zend_Config_Xml . Usando Zend_Config Per Se Normalmente, se espera que los usuarios usen una de las clases adaptadoras como Zend_Config_Ini o Zend_Config_Xml , pero si los datos de configuración están disponibles en un array PHP, se puede simplemente pasar los datos al constructor Zend_Config para utilizar una interfaz simple orientada a objetos: 'www.example.com', 'database' => array( 'adapter' => 'pdo_mysql', 'params' => array( 'host' => 'db.example.com', 'username' => 'dbuser', 'password' => 'secret', 'dbname' => 'mydatabase' ) ) ); // Crea el objeto a partir de los datos de configuración $config = new Zend_Config($configArray); // Muestra un dato de configuración (resultado: 'www.example.com') echo $config->webhost; // Use los datos de configuración para conectarse a la base de datos $db = Zend_Db::factory($config->database->adapter, $config->database->params->toArray()); // Uso alternativo: simplemente pase el objeto Zend_Config. // La Zend_Db factory sabe cómo interpretarlo. $db = Zend_Db::factory($config->database); ]]> Como se ilustra en el ejemplo de arriba, Zend_Config provee una sintáxis de propiedades de objetos anidados para acceder a datos de configuración pasados a su constructor. Junto al acceso a valores de datos orientado a objetos, Zend_Config también tiene el método get() que devolverá el valor por defecto suministrado si el elemento de datos no existe. Por ejemplo: database->get('host', 'localhost'); ]]> Usando Zend_Config con un Archivo de Configuración PHP A veces, es deseable usar un archivo de configuración puramente PHP. El código siguiente ilustra cómo podemos conseguir esto fácilmente: 'www.example.com', 'database' => array( 'adapter' => 'pdo_mysql', 'params' => array( 'host' => 'db.example.com', 'username' => 'dbuser', 'password' => 'secret', 'dbname' => 'mydatabase' ) ) ); ]]> webhost; ]]> Aspectos Teóricos Los datos de configuración se hacen accesibles al constructor Zend_Config a través de un array asociativo, que puede ser multidimensional, para permitir organizar los datos desde lo general a lo específico. Las clases de adaptador concretas permiten construir una tabla asociativa para el constructor de Zend_Config a partir de un sistema de almacenamiento de datos de configuración. Algunos scripts de usuario pueden proveer esos arrays directamente al constructor Zend_Config, sin usar una clase adaptador, lo cual puede ser apropiado en ciertas ocasiones. Cada valor del array de datos de configuración se convierte en una propiedad del objeto Zend_Config. La clave es usada como el nombre de la propiedad. Si un valor es un array por sí solo, entonces la propiedad de objeto resultante es creada como un nuevo objeto Zend_Config, cargado con los datos del array. Esto ocurre recursivamente, de forma que una jerarquía de datos de configuración puede ser creada con cualquier número de niveles. Zend_Config implementa las interfaces Countable e Iterator para facilitar el aceso sencillo a los datos de configuración. Así, uno puede usar la función count() y constructores PHP como foreach sobre objetos Zend_Config. Por defecto, los datos de configuración permitidos a través de Zend_Config son de sólo lectura, y una asignación (e.g., $config->database->host = 'example.com') provoca que se lance una excepción. Este comportamiento por defecto puede ser sobrescrito a través del constructor, sin embargo, para permitir la modificación de valores de datos. Además, cuando las modificaciones están permitidas, Zend_Config soporta el borrado de elementos (unset) (i.e. unset($config->database->host);). El método readOnly() puede ser usado para determinar si las modificaciones a un objeto Zend_Config están permitidas y el método setReadOnly() puede ser usado para evitar cualquier modificación posterior a un objeto Zend_Config que fue creado con permiso de modificaciones. Es importante no confundir tales modificaciones en memoria con guardar los datos de configuración a un medio de almacenamiento específico. Las herramientas para crear y modificar datos de configuración para distintos medios de almacenamiento están fuera del alcance de Zend_Config. Existen soluciones third-party de código abierto con el propósito de crear y modificar datos de configuración de distintos medios de almacenamiento. Las clases del adaptador heredan de la clase Zend_Config debido a que utilizan su funcionalidad. La familia de clases Zend_Config permite organizar en secciones los datos de configuración. Los objetos de adaptador Zend_Config pueden ser cargados con una sola sección especificada, múltiples secciones especificadas, o todas las secciones (si no se especifica ninguna). Las clases del adaptador Zend_Config soportan un modelo de herencia única que permite que los datos de configuración hereden de una sección de datos de configuración a otra. Esto es provisto con el fin de reducir o eliminar la necesidad de duplicar datos de configuración por distintos motivos. Una sección heredada puede también sobrescribir los valores que hereda de su sección padre. Al igual que la herencia de clases PHP, una sección puede heredar de una sección padre, la cual puede heredar de una sección abuela, etc..., pero la herencia múltiple (i.e., la sección C heredando directamente de las secciones padre A y B) no está permitida. Si tiene dos objetos Zend_Config, puede combinarlos en un único objeto usando la función merge(). Por ejemplo, dados $config y $localConfig, puede fusionar datos de $localConfig a $config usando $config->merge($localConfig);. Los ítemes en $localConfig sobrescribirán cualquier item con el mismo nombre en $config. El objeto Zend_Config que está ejecutando el merge debe haber sido construido para permitir modificaciones, pasando true como el segundo parámetro del constructor. El método setReadOnly() puede entonces ser usado para evitar cualquier modificación posterior después de que el merge se haya completado. Zend_Config_Ini Zend_Config_Ini permite a los desarrolladores almacenar datos de configuración en un formato de datos INI familiar, y leer de ellos en la aplicación usando una sintáxis de propiedades de objetos anidados. El formato INI se especializa en proveer tanto la habilidad de mantener una jerarquía de claves de datos (data keys) de configuración como la de mantener una jerarquía entre secciones de datos de configuración. Las jerarquías de datos de configuración son provistas separando las claves mediante el carácter punto ( . ). Una sección puede extender o heredar de otra sección indicando el nombre de la sección seguido de dos puntos ( : ) y el nombre de la sección desde la cual se quieren heredar los datos. parse_ini_file Zend_Config_Ini utiliza la función parse_ini_file() de PHP. Por favor, revise esta documentación para observar sus comportamientos específicos, que se propagan a Zend_Config_Ini , tales como la forma en que los valores especiales: true , false , yes , no , y null son manejados. Separador de clave Por defecto, el carácter separador de clave es el punto ( . ). Puede ser reemplazado, no obstante,cambiando la clave de $options llamada 'nestSeparator' al construir el objeto Zend_Config_Ini . Por ejemplo: Utilizando Zend_Config_Ini Este ejemplo muestra una forma de uso básica de Zend_Config_Ini para cargar datos de configuración de un archivo INI. En este ejemplo hay datos de configuración tanto para un sistema de producción como para un sistema en fase de pruebas. Debido a que los datos de la fase de pruebas son muy parecidos a los de producción, la sección de pruebas hereda de la sección de producción. En este caso, la decisión es arbitraria y podría haberse escrito a la inversa, con la sección de producción heredando de la sección de pruebas, a pesar de que éste no sería el caso para situaciones más complejas. Supongamos, entonces, que los siguientes datos de configuración están contenidos en /path/to/config.ini : Ahora, asuma que el desarrollador de aplicaciones necesita los datos de configuración de la etapa de pruebas del archivo INI. Resulta fácil cargar estos datos especificando el archivo INI en la sección de la etapa de pruebas: database->params->host; // muestra "dev.example.com" echo $config->database->params->dbname; // muestra "dbname" ]]> Parámetros del constructor Zend_Config_Ini Parámetros Notas $filename El archivo INI que se va a cargar. $section La [sección] contenida en el archivo ini que se va a cargar. Fijar este parámetro a null cargará todas las secciones. Alternativamente, se puede introducir un array de nombres de sección para cargar multiples secciones. $options = false Array de opciones. Las siguientes claves están aceptadas: allowModifications : Fijar a true para permitir modificaciones subsiguientes del archivo cargado. Por defecto es false nestSeparator : Carácter que utilizar como separador de anidamiento. Por defecto es "."
Zend_Config_Xml Zend_Config_Xml permite a los desarrolladores almacenar datos de configuración en un formato sencillo XML y leerlos a través de una sintáxis de propiedades de objetos anidados. El elemento raíz del archivo XML es irrelevante y puede ser nombrado arbitrariamente. El primer nivel de elementos XML corresponde con las secciones de datos de configuración. El formato XML admite organización jerárquica a través del anidamiento de elementos XML bajo los elementos a nivel de sección. El contenido de un elemento XML a nivel de hoja corresponde al valor de un dato de configuración. La herencia de sección está permitida por un atributo XML especial llamado extends, y el valor de este atributo se corresponde con la sección de la cual los datos son heredados por la sección extendida.. Tipo devuelto Los datos de configuración que se leen en Zend_Config_Xml son siempre devueltos como strings. La conversión de datos de string a otros tipos se deja en manos de los desarrolladores para que se ajuste a sus necesidades particulares. Usando Zend_Config_Xml Este ejemplo ilustra un uso básico de Zend_Config_Xml para cargar datos de configuración de un archivo XML. En este ejemplo hay datos de configuración tanto para un sistema de producción como para un sistema de pruebas. Debido a que los datos de configuración del sistema de pruebas son muy similares a los de producción, la sección de pruebas hereda de la sección de producción. En este caso, la decisión es arbitraria y podría haberse escrito a la inversa, con la sección de producción heredando de la sección de pruebas, a pesar de que éste no sería el caso para situaciones más complejas. Suponga, pues, que los datos de configuración siguientes están contenidos en /ruta/de/config.xml: www.example.com pdo_mysql db.example.com dbuser secret dbname dev.example.com devuser devsecret ]]> Ahora, asuma que el desarrollador de aplicaciones necesita los datos de configuración de la fase de pruebas del archivo XML. Es una tarea sencilla cargar estos datos, especificando el archivo XML y la sección de pruebas: database->params->host; // muestra "dev.example.com" echo $config->database->params->dbname; // muestra "dbname" ]]> Usando atributos de etiqueta en Zend_Config_Xml Zend_Config_Xml también soporta dos formas adicionales de definir nodos en la configuración. Ambas hacen uso de atributos. Dado que los atributos extends y value son palabras reservadas (la última por la segunda manera de usar atributos), pueden no ser utilizadas. La primera manera de utilizar atributos es añadir atributos en un nodo padre, el cual será interpretado como hijo de ese nodo: ]]> La otra forma no reduce la configuración, sino que permite mantenerla de forma más fácil dado que no es necesario escribir el nombre de la etiqueta dos veces. Simplemente, cree una etiqueta vacía con el valor en el atributo value: www.example.com ]]>
Zend_Config_Writer Zend_Console_Getopt Zend_Controller Zend_Controller Quick Start Introducción Zend_Controller es el corazón del sistema de MVC de Zend Framework MVC. MVC son las siglas de Modelo-Vista-Controlador y es un patrón de diseño con el objetivo de separar la lógica de la aplicación de la lógica de visualización. Zend_Controller_Front implementa el patrón Front Controller (Controlador Frontal) en el cual todas las transacciones HTTP (requests) son interceptadas por el controlador frontal y despachado a una Acción particular de un Controlador según la URL pedida. El sistema Zend_Controller fue construido con la extensibilidad en mente, ya sea heredando las clases existentes, escribiendo nuevas clases que implementan varias interfaces o clases abstractas que forman la base de la familia de clases del controlador, o escribiendo plugins o helpers de las acciones para aumentar o manipular la funcionalidad del sistema. Quick Start Si necesita información más detallada, mire las secciones siguientes. Si solamente quiere inicializar y ejecutar una aplicación rápidamente, siga leyendo. Cree su estructura de archivos El primer paso es crear su estructura de archivos. La estructura típica es la siguiente: Establezca su document root Apunte su document root en su servidor web hacia el directorio html de la estrctura de archivos de arriba. Cree sus reglas de reescritura Edite el archivo html/.htaccess que aparece arriba de la siguiente forma: La regla de arriba redigirá las peticiones a recuros existentes (enlaces simbólicos existentes, archivos no vacíos, o directorios no vacíos) en consecuencia, y todas las otras peticiones al front controller. Las reglas de arriba pertenecen a Apache. Para ejemplos de reglas de rewrite para otros servidores web, mire la documentación de router . Cree su archivo bootstrap El archivo bootstrap es la página a la que todas las peticiones son redirigidas a través de -- html/index.php en este caso. Abra el archivo html/index.php en el editor de su elección y añada lo siguiente: Esto instanciará y hará un dispatch del front controller, que redigirá las peticiones a los action controllers. Cree su action controller por defecto Antes de tratar los action controllers, debe primero entender cómo las peticiones son redirigidas en Zend Framework. Por defecto, el primero segmento de una ruta URL apunta a un controlador, y el segundo a una acción. Por ejemplo, dada la URL http://framework.zend.com/roadmap/components , la ruta es /roadmap/components , que apuntará al controlador roadmap y la acción components . Si no se suministra una acción, se asume la acción index , y si no se suministra un controlador, se asume el controlador index (siguiendo la convención de Apache de apuntar a DirectoryIndex automáticamente). El dispatcher de Zend_Controller toma entonces el valor del controlador y lo apunta a una clase. Por defecto, pone en mayúsculas la primera letra del nombre de controlador y agrega la palabra Controller . De esta forma, en nuestro ejemplo de arriba, el controlador roadmap es dirigido a la clase RoadmapController . De la misma forma, el valor de action es dirigido a un método de la clase controladora. Por defecto, el valor se pasa a minúsculas, y la palabra Action es añadida. De esta forma, en nuestro ejemplo de arriba, la acción components se convierte en componentsAction , y el método final llamado es RoadmapController::componentsAction() . Continuando, creemos ahora un action controller y un método de acción por defecto. Como se ha dicho antes, el controlador por defecto y la acción llamada son ambos index . Abra el archivo application/controllers/IndexController.php , e introduzca lo siguiente: Por defecto, el action helper ViewRenderer está activado. Esto significa que simplemente definiendo un action method y un view script correspondiente, tendrá su contenido generado inmediatamente. Por defecto, Zend_View es usado como la capa Vista en el patrón MVC. El ViewRenderer hace algo de magia, y usa el nombre de controlador (e.g., index ) y el nombre de acción actual (e.g., index ) para determinar qué plantilla traer. Por defecto, las plantillas terminan con la extensión .phtml , lo que significa que en el ejemplo de arriba, la plantilla index/index.phtml será generada. Adicionalmente, el ViewRenderer asume automáticamente que la carpeta views al mismo nivel que la carpeta controller será la carpeta raíz de la vista, y que el script de vista actual estará en la subcarpeta views/scripts/. De esta forma, la plantilla generada será encontrada en application/views/scripts/index/index.phtml . Cree su view script Como hemos mencionado en la sección anterior , los scripts de vista se encuentran en application/views/scripts/ ; el view script para el controlador y la acción por defecto está en application/views/scripts/index/index.phtml . Cree este archivo, y escriba un poco de HTML: Mi primera aplicación Zend Framework

>¡Hola, Mundo!

]]>
Cree su controlador de errores Por defecto, está registrado el plugin 'error handler' . Este plugin espera que exista un controlador para manejar los errores. Por defecto, asume un ErrorController en el módulo default con un método errorAction : Asumiendo el sistema de carpetas discutido anteriormente, este archivo irá en application/controllers/ErrorController.php . También necesitará crear un view script en application/views/scripts/error/error.phtml ; el contenido de ejemplo será parecido a: Error

Ocurrió un error

Ocurrió un error; Por favor, inténtelo de nuevo más tarde.

]]>
¡Vea el sitio! Con su primer controlador y vista, ya puede arrancar su navegador y acceder a su sitio. Asumiendo que example.com es su dominio, cualquiera de las siguientes URLs le llevará a la página que acaba de crear: http://example.com/ http://example.com/index http://example.com/index/index Ya está listo para empezar a crear más métodos de controladores y acciones. ¡Felicidades!
Zend_Currency Zend_Date Introducción El componente Zend_Date ofrece una API detallada pero simple para manipular fechas y horas. Sus métodos aceptan una gran variedad de tipos de información, incluyendo partes de fecha, en numerosas combinaciones provocando muchas características y posibilidades más allá de las funciones de fecha PHP relacionadas. Para las últimas actualizaciones manuales, por favor ver el siguiente link url="http://framework.zend.com/wiki/display/ZFDOCDEV/Home">our online manual (sincronizado frecuentemente con Subversion) . Aunque la simplicidad sea el objetivo, trabajar con fechas y tiempos localizados mientras se modifican, combinan y comparan partes, provoca una complejidad inevitable. Las fechas, así como los tiempos, a menudo son escritos de forma diferente en zonas locales distintas. Por ejemplo, algunos colocan primero el mes, mientras otros escriben el año en primer lugar cuando expresan fechas del calendario. Para más información relacionada con manejo de localizaciones y normalización, por favor vea el manual de Zend_Locale . Zend_Date también soporta nombres de meses abreviados en varios idiomas. Zend_Locale facilita la normalización de meses localizados y nombres de días de la semana a timestamps, los cuales pueden, a su vez, ser mostrados localizados a otras regiones. Asigne Siempre una Zona Horaria por Defecto Antes de utilizar funciones relacionadas con fechas en PHP o en el Zend Framework, primero debe asegurarse que su aplicación tiene una zona horaria correcta por defecto, configurando la variable de entorno TZ, usando el parametro del php.ini date.timezone , o usando date_default_timezone_set() . En PHP, podemos ajustar todas las funciones relacionadas con fechas y hora para trabajar para un usuario particular configurando por defecto una zona horaria de acuerdo a las expectativas del usuario. Para una lista completa de configuraciones de zona horaria, vea el siguiente link Lista de Identificadores de Zonas Horarias CLDR . Configurando una Zona Horaria por Defecto ¡Al crear instancias de Zend_Date, su zona horaria se convertirá automáticamente en la zona horaria por defecto actual! De esta forma, la configuración de zona horaria tendrá en cuenta cualquier cambio de hora de invierno/verano (Daylight Saving Time, DST), eliminando la necesidad de especificarlo explícitamente. Tenga en cuenta que las zonas horarias UTC y GMT no incluyen el cambio de hora de invierno/verano (Daylight Saving Time, DST). Esto significa que aunque defina a mano que Zend_Date deba trabajar con DST, podría ser anulado por las instancias de Zend_Date que han sido fijadas a UTC o GMT. ¿Por Qué Usar Zend_Date? Zend_Date ofrece las siguientes prestaciones, las cuales extienden el alcance de las funciones de fecha de PHP: API sencilla Zend_Date aporta una API muy sencilla, que combina lo mejor de la funcionalidad fecha/hora de cuatro lenguajes de programación. Es posible, por ejemplo, añadir o comparar dos horas dentro de una misma columna. Completamente internacionalizado Todos los nombres de meses y días de la semana completos y abreviados están incluidos para más de 130 idiomas. Los métodos admiten tanto entrada como salida de fechas usando los nombres localizados de meses y días de la semana. Timestamps ilimitados A pesar de que la documentación de PHP 5.2 indice: "El intervalo de valores admitidos de timestamps es desde el 13 Dec 1901 20:45:54 GMT al 19 Ene 2038 03:14:07 GMT," Zend_Date admite un rango casi ilimitado, con la ayuda de la extensión BCMath. Si BCMath no está disponible, Zend_Date tendrá una funcionalidad de timestamps reducida al rango del tipo float soportado por su servidor. El tamaño de un float es dependiente de la plataforma, aunque un máximo de ~1.8e308 con una precisión de cerca de 14 dígitos decimales es un valor habitual (formato 64 bit IEEE)." [ http://www.php.net/float ]. Adicionalmente, las limitaciones heredadas de los tipos de dato float, y errores de redondeo de números flotantes pueden introducir errores en los cálculos. Para evitar estos problemas, los componentes ZF I18n usan la extensión BCMath, si está disponible. Soporte para especificaciones de fecha ISO_8601 Las especificaciones de fecha ISO_8601 están aceptadas. Incluso las especificaciones de fecha ISO_8601 parcialmente autorizadas serán identificadas. Estos formatos de fecha son particularmente útiles al trabajar con bases de datos. Por ejemplo, aunque MsSQL y MySQL difieren ligeramente uno de otro, ambos tienen soporte por parte de Zend_Date usando la constante de especificación de formato Zend_Date::ISO_8601. Cuando las cadenas de fecha sean del tipo "Y/m/d" o "Y-m-d H:i:s", de acuerdo con los tokens de formato PHP date(), use el soporte integrado de Zend_Date para fechas formateadas ISO 8601. Calcular amanecer y puesta de sol Las horas de amanecer y puesta de sol pueden mostrarse para cualquier lugar y día, para que no pierda ni un segundo de luz diurna para trabajar en su proyecto PHP favorito :) Zend_Db Zend_Db_Adapter Zend_Db y sus clases relacionadas proporcionan una interfaz simple de base de datos SQL para Zend Framework. El Zend_Db_Adapter es la clase base que se utiliza para conectar su aplicación PHP A una base de datos (RDBMS). Existen diferentes clases Adapters(Adaptador) para cada tipo de base de datos (RDBMS). Las clases Adapters de Zend_Db crean un puente entre las extensiones de base de datos de PHP hacia una interfaz común, para ayudarle a escribir aplicaciones PHP una sola vez y poder desplegar múltiples tipos de base de datos (RDBMS) con muy poco esfuerzo. La Interfaz de la clase adaptador (adapter) es similar a la intefaz de la extensión PHP Data Objects . Zend_Db proporciona clases Adaptadoras para los drivers PDO de los siguientes tipos de RDBMS: IBM DB2 e Informix Dynamic Server (IDS), usando la extensión PHP pdo_ibm MySQL, usando la extensión PHP pdo_mysql Microsoft SQL Server, usando la extensión PHP pdo_mssql Oracle, usando la extensión PHP pdo_oci PostgreSQL, usando la extensión PHP pdo_pgsql SQLite, usando la extensión PHP pdo_sqlite Ademas, Zend_Db proporciona clases Adaptadoras que utilizan las extensiones de base de datos de PHP de los siguientes tipos: MySQL, usando la extensión PHP mysqli Oracle, usando la extensión PHP oci8 IBM DB2, usando la extensión PHP ibm_db2 Firebird/Interbase, usando la extensión PHP php_interbase Cada Zend_Db_Adaptador utiliza una extensión PHP. Se debe de tener habilitada la respectiva extensión en su entorno PHP para utilizar un Zend_Db_Adapter. Por ejemplo, si se utiliza una clase Zend_Db_Adapter basada en PDO, tiene que habilitar tanto la extensión PDO como el driver PDO del tipo de base de datos que se utiliza. Conexión a una Base de Datos utilizando un Adaptador Esta sección describe cómo crear una instancia de un Adaptador de base de datos. Esto corresponde a establecer una conexión a un servidor de Base de Datos (RDBMS) desde su aplicación PHP. Usando un Constructor de Zend_Db Adapter Se puede crear una instancia de un Adaptador utilizando su constructor. Un constructor de adaptador toma un argumento, que es un conjunto de parámetros utilizados para declarar la conexión. Usando el Constructor de un Adaptador '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test' )); ]]> Usando el Factory de Zend_Db Como alternativa a la utilización directa del constructor de un adaptador, se puede crear una instancia del adaptador que use el método estático Zend_Db::factory() . Este método carga dinámicamente el archivo de clase Adaptador bajo demanda, usando Zend_Loader::loadClass() . El primer argumento es una cadena que nombra al nombre base de la clase Adaptador. Por ejemplo, la cadena 'Pdo_Mysql' corresponde a la clase Zend_Db_Adapter_Pdo_Mysql. El segundo argumento es el mismo array de parámetros que hubiera enviado al constructor del adaptador. Usando el Adaptador del método factory '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test' )); ]]> Si crea su propia clase que extiende a Zend_Db_Adapter_Abstract, pero no nombra su clase con el prefijo de paquete "Zend_Db_Adapter", se puede utilizar el método factory() para cargar su adaptador si se especifica la parte principal de la clase del adaptador con la clave "adapterNamespace" en el conjunto de parámetros Usando el método factory para una clase Adaptador personalizada '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test', 'adapterNamespace' => 'MyProject_Db_Adapter' )); ]]> Uso de Zend_Config con Zend_Db Factory Opcionalmente, se puede especificar cualquier argumento del método factory() como un objeto de tipo Zend_Config . Si el primer argumento es un objeto de configuración, se espera que contenga una propiedad llamada adapter , conteniendo la cadena que da nombre al nombre base de la clase de adaptador. Opcionalmente, el objeto puede contener una propiedad llamada params , con subpropiedades correspondientes a nombres de parámetros del adaptador. Esto es usado sólo si el segundo argumento del método factory() se ha omitido. Uso del método factory del Adaptador con un objeto Zend_Config En el siguiente ejemplo, un objeto Zend_Config es creado usando un array. También puedes cargar los datos de un archivo externo, por ejemplo con Zend_Config_Ini o Zend_Config_Xml . array( 'adapter' => 'Mysqli', 'params' => array( 'dbname' => 'test', 'username' => 'webuser', 'password' => 'secret', ) ) ) ); $db = Zend_Db::factory($config->database); ]]> El segundo argumento del método factory() puede ser un array asociativo con entradas correspondientes a los parámetros del adaptador. Este argumento es opcional. Si el primer argumento es de tipo Zend_Config, se asume que tiene todos los parametros, y el segundo argumento es ignorado. Parámetros del Adaptador El siguiente listado explica parámetros comunes reconocidos por Adaptador de clases Zend_Db. host : una string conteniendo un nombre de host o dirección IP del servidor de base de datos. Si la base de datos está corriendo sobre el mismo host que la aplicación PHP, usted puede utilizar 'localhost' o '127.0.0.1'. username : identificador de cuenta para autenticar una conexión al servidor RDBMS. password : la contraseña de la cuenta para la autenticación de credenciales de conexión con el servidor RDBMS dbname : nombre de la base de datos en el servidor RDBMS. port : algunos servidores RDBMS pueden aceptar conexiones de red sobre un número de puerto específico. El parámetro del puerto le permite especificar el puerto al que su aplicación PHP se conecta, para que concuerde el puerto configurado en el servidor RDBMS. options : este parámetro es un array asociativo de opciones que son genéricas a todas las clases Zend_Db_Adapter. driver_options : este parámetro es un array asociativo de opciones adicionales para una extensión de base de datos dada. un uso típico de este parámetro es establecer atributos de un driver PDO. adapterNamespace : nombre de la parte inicial del nombre de las clase para el adaptador, en lugar de 'Zend_Db_Adapter'. Utilice esto si usted necesita usar el método factory() para cargar un adaptador de clase de base de datos que no sea de Zend. Passing the case-folding option to the factory Usted puede pasar esta opción específica por la constante Zend_Db::CASE_FOLDING . Este corresponde al atributo ATTR_CASE en los drivers de base de datos PDO e IBM DB2, ajustando la sensibilidad de las claves tipo cadena en los resultados de consultas. La opción toma los valores Zend_Db::CASE_NATURAL (el predeterminado), Zend_Db::CASE_UPPER , y Zend_Db::CASE_LOWER . Zend_Db::CASE_UPPER ); $params = array( 'host' => '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test', 'options' => $options ); $db = Zend_Db::factory('Db2', $params); ]]> Passing the auto-quoting option to the factory Usted puede especificar esta opción por la constante Zend_Db::AUTO_QUOTE_IDENTIFIERS . Si el valor es true (el predeterminado), los identificadores como nombres de tabla, nombres de columna, e incluso los alias son delimitados en la sintaxis SQL generada por el Adatador del objeto. Esto hace que sea sencillo utilizar identificadores que contengan palabras reservadas de SQL, o caracteres especiales. Si el valor es false , los identificadores no son delimitados automáticamente. Si usted necesita delimitar identificadores, debe hacer usted mismo utilizando el método quoteIdentifier() . false ); $params = array( 'host' => '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test', 'options' => $options ); $db = Zend_Db::factory('Pdo_Mysql', $params); ]]> Passing PDO driver options to the factory true ); $params = array( 'host' => '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test', 'driver_options' => $pdoParams ); $db = Zend_Db::factory('Pdo_Mysql', $params); echo $db->getConnection() ->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY); ]]> Managing Lazy Connections Creating an instance of an Adapter class does not immediately connect to the RDBMS server. The Adapter saves the connection parameters, and makes the actual connection on demand, the first time you need to execute a query. This ensures that creating an Adapter object is quick and inexpensive. You can create an instance of an Adapter even if you are not certain that you need to run any database queries during the current request your application is serving. If you need to force the Adapter to connect to the RDBMS, use the getConnection() method. This method returns an object for the connection as represented by the respective PHP database extension. For example, if you use any of the Adapter classes for PDO drivers, then getConnection() returns the PDO object, after initiating it as a live connection to the specific database. It can be useful to force the connection if you want to catch any exceptions it throws as a result of invalid account credentials, or other failure to connect to the RDBMS server. These exceptions are not thrown until the connection is made, so it can help simplify your application code if you handle the exceptions in one place, instead of at the time of the first query against the database. Handling connection exceptions getConnection(); } catch (Zend_Db_Adapter_Exception $e) { // perhaps a failed login credential, or perhaps the RDBMS is not running } catch (Zend_Exception $e) { // perhaps factory() failed to load the specified Adapter class } ]]> La base de datos de ejemplo En la documentación de las clases Zend_Db, usamos un conjunto sencillo de tablas para ilustrar el uso de las clases y métodos. Estas tablas de ejemplo permiten almacenar información para localizar bugs en un proyecto de desarrollo de software. La base de datos contiene cuatro tablas: accounts almacena información sobre cada usuario que hace el seguimiento de bugs. products almacena información sobre cada producto para el que pueden registrarse bugs. bugs almacena información sobre bugs, incluyendo el estado actual del bug, la persona que informó sobre el bug, la persona que está asignada para corregir el bug, y la persona que está asignada para verificar la corrección. bugs_products stores a relationship between bugs and products. This implements a many-to-many relationship, because a given bug may be relevant to multiple products, and of course a given product can have multiple bugs. La siguiente definición de datos SQL en lenguaje pseudocódigo describe las tablas de esta base de datos de ejemplo. Estas tablas de ejemplo son usadas ampliamente por los tests unitarios automatizados de Zend_Db. Also notice that the bugs table contains multiple foreign key references to the accounts table. Each of these foreign keys may reference a different row in the accounts table for a given bug. The diagram below illustrates the physical data model of the example database. Reading Query Results This section describes methods of the Adapter class with which you can run SELECT queries and retrieve the query results. Fetching a Complete Result Set You can run a SQL SELECT query and retrieve its results in one step using the fetchAll() method. The first argument to this method is a string containing a SELECT statement. Alternatively, the first argument can be an object of class Zend_Db_Select . The Adapter automatically converts this object to a string representation of the SELECT statement. The second argument to fetchAll() is an array of values to substitute for parameter placeholders in the SQL statement. Using fetchAll() fetchAll($sql, 2); ]]> Changing the Fetch Mode By default, fetchAll() returns an array of rows, each of which is an associative array. The keys of the associative array are the columns or column aliases named in the select query. You can specify a different style of fetching results using the setFetchMode() method. The modes supported are identified by constants: Zend_Db::FETCH_ASSOC : return data in an array of associative arrays. The array keys are column names, as strings. This is the default fetch mode for Zend_Db_Adapter classes. Note that if your select-list contains more than one column with the same name, for example if they are from two different tables in a JOIN, there can be only one entry in the associative array for a given name. If you use the FETCH_ASSOC mode, you should specify column aliases in your SELECT query to ensure that the names result in unique array keys. By default, these strings are returned as they are returned by the database driver. This is typically the spelling of the column in the RDBMS server. You can specify the case for these strings, using the Zend_Db::CASE_FOLDING option. Specify this when instantiating the Adapter. See . Zend_Db::FETCH_NUM : return data in an array of arrays. The arrays are indexed by integers, corresponding to the position of the respective field in the select-list of the query. Zend_Db::FETCH_BOTH : return data in an array of arrays. The array keys are both strings as used in the FETCH_ASSOC mode, and integers as used in the FETCH_NUM mode. Note that the number of elements in the array is double that which would be in the array if you used either FETCH_ASSOC or FETCH_NUM. Zend_Db::FETCH_COLUMN : return data in an array of values. The value in each array is the value returned by one column of the result set. By default, this is the first column, indexed by 0. Zend_Db::FETCH_OBJ : return data in an array of objects. The default class is the PHP built-in class stdClass. Columns of the result set are available as public properties of the object. Using setFetchMode() setFetchMode(Zend_Db::FETCH_OBJ); $result = $db->fetchAll('SELECT * FROM bugs WHERE bug_id = ?', 2); // $result is an array of objects echo $result[0]->bug_description; ]]> Fetching a Result Set as an Associative Array The fetchAssoc() method returns data in an array of associative arrays, regardless of what value you have set for the fetch mode. Using fetchAssoc() setFetchMode(Zend_Db::FETCH_OBJ); $result = $db->fetchAssoc('SELECT * FROM bugs WHERE bug_id = ?', 2); // $result is an array of associative arrays, in spite of the fetch mode echo $result[0]['bug_description']; ]]> Fetching a Single Column from a Result Set The fetchCol() method returns data in an array of values, regardless of the value you have set for the fetch mode. This only returns the first column returned by the query. Any other columns returned by the query are discarded. If you need to return a column other than the first, see . Using fetchCol() setFetchMode(Zend_Db::FETCH_OBJ); $result = $db->fetchCol( 'SELECT bug_description, bug_id FROM bugs WHERE bug_id = ?', 2); // contains bug_description; bug_id is not returned echo $result[0]; ]]> Fetching Key-Value Pairs from a Result Set The fetchPairs() method returns data in an array of key-value pairs, as an associative array with a single entry per row. The key of this associative array is taken from the first column returned by the SELECT query. The value is taken from the second column returned by the SELECT query. Any other columns returned by the query are discarded. You should design the SELECT query so that the first column returned has unique values. If there are duplicates values in the first column, entries in the associative array will be overwritten. Using fetchPairs() setFetchMode(Zend_Db::FETCH_OBJ); $result = $db->fetchPairs('SELECT bug_id, bug_status FROM bugs'); echo $result[2]; ]]> Fetching a Single Row from a Result Set The fetchRow() method returns data using the current fetch mode, but it returns only the first row fetched from the result set. Using fetchRow() setFetchMode(Zend_Db::FETCH_OBJ); $result = $db->fetchRow('SELECT * FROM bugs WHERE bug_id = 2'); // note that $result is a single object, not an array of objects echo $result->bug_description; ]]> Fetching a Single Scalar from a Result Set The fetchOne() method is like a combination of fetchRow() with fetchCol() , in that it returns data only for the first row fetched from the result set, and it returns only the value of the first column in that row. Therefore it returns only a single scalar value, not an array or an object. Using fetchOne() fetchOne('SELECT bug_status FROM bugs WHERE bug_id = 2'); // this is a single string value echo $result; ]]> Writing Changes to the Database You can use the Adapter class to write new data or change existing data in your database. This section describes methods to do these operations. Inserting Data You can add new rows to a table in your database using the insert() method. The first argument is a string that names the table, and the second argument is an associative array, mapping column names to data values. Inserting to a table '2007-03-22', 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); $db->insert('bugs', $data); ]]> Columns you exclude from the array of data are not specified to the database. Therefore, they follow the same rules that an SQL INSERT statement follows: if the column has a DEFAULT clause, the column takes that value in the row created, otherwise the column is left in a NULL state. By default, the values in your data array are inserted using parameters. This reduces risk of some types of security issues. You don't need to apply escaping or quoting to values in the data array. You might need values in the data array to be treated as SQL expressions, in which case they should not be quoted. By default, all data values passed as strings are treated as string literals. To specify that the value is an SQL expression and therefore should not be quoted, pass the value in the data array as an object of type Zend_Db_Expr instead of a plain string. Inserting expressions to a table new Zend_Db_Expr('CURDATE()'), 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); $db->insert('bugs', $data); ]]> Retrieving a Generated Value Some RDBMS brands support auto-incrementing primary keys. A table defined this way generates a primary key value automatically during an INSERT of a new row. The return value of the insert() method is not the last inserted ID, because the table might not have an auto-incremented column. Instead, the return value is the number of rows affected (usually 1). If your table is defined with an auto-incrementing primary key, you can call the lastInsertId() method after the insert. This method returns the last value generated in the scope of the current database connection. Using lastInsertId() for an auto-increment key insert('bugs', $data); // return the last value generated by an auto-increment column $id = $db->lastInsertId(); ]]> Some RDBMS brands support a sequence object, which generates unique values to serve as primary key values. To support sequences, the lastInsertId() method accepts two optional string arguments. These arguments name the table and the column, assuming you have followed the convention that a sequence is named using the table and column names for which the sequence generates values, and a suffix "_seq". This is based on the convention used by PostgreSQL when naming sequences for SERIAL columns. For example, a table "bugs" with primary key column "bug_id" would use a sequence named "bugs_bug_id_seq". Using lastInsertId() for a sequence insert('bugs', $data); // return the last value generated by sequence 'bugs_bug_id_seq'. $id = $db->lastInsertId('bugs', 'bug_id'); // alternatively, return the last value generated by sequence 'bugs_seq'. $id = $db->lastInsertId('bugs'); ]]> If the name of your sequence object does not follow this naming convention, use the lastSequenceId() method instead. This method takes a single string argument, naming the sequence literally. Using lastSequenceId() insert('bugs', $data); // return the last value generated by sequence 'bugs_id_gen'. $id = $db->lastSequenceId('bugs_id_gen'); ]]> For RDBMS brands that don't support sequences, including MySQL, Microsoft SQL Server, and SQLite, the arguments to the lastInsertId() method are ignored, and the value returned is the most recent value generated for any table by INSERT operations during the current connection. For these RDBMS brands, the lastSequenceId() method always returns null . Why not use "SELECT MAX(id) FROM table"? Sometimes this query returns the most recent primary key value inserted into the table. However, this technique is not safe to use in an environment where multiple clients are inserting records to the database. It is possible, and therefore is bound to happen eventually, that another client inserts another row in the instant between the insert performed by your client application and your query for the MAX(id) value. Thus the value returned does not identify the row you inserted, it identifies the row inserted by some other client. There is no way to know when this has happened. Using a strong transaction isolation mode such as "repeatable read" can mitigate this risk, but some RDBMS brands don't support the transaction isolation required for this, or else your application may use a lower transaction isolation mode by design. Furthermore, using an expression like "MAX(id)+1" to generate a new value for a primary key is not safe, because two clients could do this query simultaneously, and then both use the same calculated value for their next INSERT operation. All RDBMS brands provide mechanisms to generate unique values, and to return the last value generated. These mechanisms necessarily work outside of the scope of transaction isolation, so there is no chance of two clients generating the same value, and there is no chance that the value generated by another client could be reported to your client's connection as the last value generated. Updating Data You can update rows in a database table using the update() method of an Adapter. This method takes three arguments: the first is the name of the table; the second is an associative array mapping columns to change to new values to assign to these columns. The values in the data array are treated as string literals. See for information on using SQL expressions in the data array. The third argument is a string containing an SQL expression that is used as criteria for the rows to change. The values and identifiers in this argument are not quoted or escaped. You are responsible for ensuring that any dynamic content is interpolated into this string safely. See for methods to help you do this. The return value is the number of rows affected by the update operation. Updating rows '2007-03-23', 'bug_status' => 'FIXED' ); $n = $db->update('bugs', $data, 'bug_id = 2'); ]]> If you omit the third argument, then all rows in the database table are updated with the values specified in the data array. If you provide an array of strings as the third argument, these strings are joined together as terms in an expression separated by AND operators. Updating rows using an array of expressions '2007-03-23', 'bug_status' => 'FIXED' ); $where[] = "reported_by = 'goofy'"; $where[] = "bug_status = 'OPEN'"; $n = $db->update('bugs', $data, $where); // Resulting SQL is: // UPDATE "bugs" SET "update_on" = '2007-03-23', "bug_status" = 'FIXED' // WHERE ("reported_by" = 'goofy') AND ("bug_status" = 'OPEN') ]]> Deleting Data You can delete rows from a database table using the delete() method. This method takes two arguments: the first is a string naming the table. The second argument is a string containing an SQL expression that is used as criteria for the rows to delete. The values and identifiers in this argument are not quoted or escaped. You are responsible for ensuring that any dynamic content is interpolated into this string safely. See for methods to help you do this. The return value is the number of rows affected by the delete operation. Deleting rows delete('bugs', 'bug_id = 3'); ]]> If you omit the second argument, the result is that all rows in the database table are deleted. If you provide an array of strings as the second argument, these strings are joined together as terms in an expression separated by AND operators. Quoting Values and Identifiers When you form SQL queries, often it is the case that you need to include the values of PHP variables in SQL expressions. This is risky, because if the value in a PHP string contains certain symbols, such as the quote symbol, it could result in invalid SQL. For example, notice the imbalanced quote characters in the following query: Even worse is the risk that such code mistakes might be exploited deliberately by a person who is trying to manipulate the function of your web application. If they can specify the value of a PHP variable through the use of an HTTP parameter or other mechanism, they might be able to make your SQL queries do things that you didn't intend them to do, such as return data to which the person should not have privilege to read. This is a serious and widespread technique for violating application security, known as "SQL Injection" (see http://en.wikipedia.org/wiki/SQL_Injection ). The Zend_Db Adapter class provides convenient functions to help you reduce vulnerabilities to SQL Injection attacks in your PHP code. The solution is to escape special characters such as quotes in PHP values before they are interpolated into your SQL strings. This protects against both accidental and deliberate manipulation of SQL strings by PHP variables that contain special characters. Using <code>quote()</code> The quote() method accepts a single argument, a scalar string value. It returns the value with special characters escaped in a manner appropriate for the RDBMS you are using, and surrounded by string value delimiters. The standard SQL string value delimiter is the single-quote ( ' ). Using quote() quote("O'Reilly"); echo $name; // 'O\'Reilly' $sql = "SELECT * FROM bugs WHERE reported_by = $name"; echo $sql; // SELECT * FROM bugs WHERE reported_by = 'O\'Reilly' ]]> Note that the return value of quote() includes the quote delimiters around the string. This is different from some functions that escape special characters but do not add the quote delimiters, for example mysql_real_escape_string() . Values may need to be quoted or not quoted according to the SQL datatype context in which they are used. For instance, in some RDBMS brands, an integer value must not be quoted as a string if it is compared to an integer-type column or expression. In other words, the following is an error in some SQL implementations, assuming intColumn has a SQL datatype of INTEGER You can use the optional second argument to the quote() method to apply quoting selectively for the SQL datatype you specify. Using quote() with a SQL type quote($value, 'INTEGER'); ]]> Each Zend_Db_Adapter class has encoded the names of numeric SQL datatypes for the respective brand of RDBMS. You can also use the constants Zend_Db::INT_TYPE , Zend_Db::BIGINT_TYPE , and Zend_Db::FLOAT_TYPE to write code in a more RDBMS-independent way. Zend_Db_Table specifies SQL types to quote() automatically when generating SQL queries that reference a table's key columns. Using <code>quoteInto()</code> The most typical usage of quoting is to interpolate a PHP variable into a SQL expression or statement. You can use the quoteInto() method to do this in one step. This method takes two arguments: the first argument is a string containing a placeholder symbol ( ? ), and the second argument is a value or PHP variable that should be substituted for that placeholder. The placeholder symbol is the same symbol used by many RDBMS brands for positional parameters, but the quoteInto() method only emulates query parameters. The method simply interpolates the value into the string, escapes special characters, and applies quotes around it. True query parameters maintain the separation between the SQL string and the parameters as the statement is parsed in the RDBMS server. Using quoteInto() quoteInto("SELECT * FROM bugs WHERE reported_by = ?", "O'Reilly"); echo $sql; // SELECT * FROM bugs WHERE reported_by = 'O\'Reilly' ]]> You can use the optional third parameter of quoteInto() to specify the SQL datatype. Numeric datatypes are not quoted, and other types are quoted. Using quoteInto() with a SQL type quoteInto("SELECT * FROM bugs WHERE bug_id = ?", '1234', 'INTEGER'); echo $sql; // SELECT * FROM bugs WHERE reported_by = 1234 ]]> Using <code>quoteIdentifier()</code> Values are not the only part of SQL syntax that might need to be variable. If you use PHP variables to name tables, columns, or other identifiers in your SQL statements, you might need to quote these strings too. By default, SQL identifiers have syntax rules like PHP and most other programming languages. For example, identifiers should not contain spaces, certain punctuation or special characters, or international characters. Also certain words are reserved for SQL syntax, and should not be used as identifiers. However, SQL has a feature called delimited identifiers , which allows broader choices for the spelling of identifiers. If you enclose a SQL identifier in the proper types of quotes, you can use identifiers with spellings that would be invalid without the quotes. Delimited identifiers can contain spaces, punctuation, or international characters. You can also use SQL reserved words if you enclose them in identifier delimiters. The quoteIdentifier() method works like quote() , but it applies the identifier delimiter characters to the string according to the type of Adapter you use. For example, standard SQL uses double-quotes ( " ) for identifier delimiters, and most RDBMS brands use that symbol. MySQL uses back-quotes ( ` ) by default. The quoteIdentifier() method also escapes special characters within the string argument. Using quoteIdentifier() quoteIdentifier("order"); $sql = "SELECT * FROM $tableName"; echo $sql // SELECT * FROM "order" ]]> SQL delimited identifiers are case-sensitive, unlike unquoted identifiers. Therefore, if you use delimited identifiers, you must use the spelling of the identifier exactly as it is stored in your schema, including the case of the letters. In most cases where SQL is generated within Zend_Db classes, the default is that all identifiers are delimited automatically. You can change this behavior with the option Zend_Db::AUTO_QUOTE_IDENTIFIERS . Specify this when instantiating the Adapter. See . Controlling Database Transactions Databases define transactions as logical units of work that can be committed or rolled back as a single change, even if they operate on multiple tables. All queries to a database are executed within the context of a transaction, even if the database driver manages them implicitly. This is called auto-commit mode, in which the database driver creates a transaction for every statement you execute, and commits that transaction after your SQL statement has been executed. By default, all Zend_Db Adapter classes operate in auto-commit mode. Alternatively, you can specify the beginning and resolution of a transaction, and thus control how many SQL queries are included in a single group that is committed (or rolled back) as a single operation. Use the beginTransaction() method to initiate a transaction. Subsequent SQL statements are executed in the context of the same transaction until you resolve it explicitly. To resolve the transaction, use either the commit() or rollBack() methods. The commit() method marks changes made during your transaction as committed, which means the effects of these changes are shown in queries run in other transactions. The rollBack() method does the opposite: it discards the changes made during your transaction. The changes are effectively undone, and the state of the data returns to how it was before you began your transaction. However, rolling back your transaction has no effect on changes made by other transactions running concurrently. After you resolve this transaction, Zend_Db_Adapter returns to auto-commit mode until you call beginTransaction() again. Managing a transaction to ensure consistency beginTransaction(); try { // Attempt to execute one or more queries: $db->query(...); $db->query(...); $db->query(...); // If all succeed, commit the transaction and all changes // are committed at once. $db->commit(); } catch (Exception $e) { // If any of the queries failed and threw an exception, // we want to roll back the whole transaction, reversing // changes made in the transaction, even those that succeeded. // Thus all changes are committed together, or none are. $db->rollBack(); echo $e->getMessage(); } ]]> Listing and Describing Tables The listTables() method returns an array of strings, naming all tables in the current database. The describeTable() method returns an associative array of metadata about a table. Specify the name of the table as a string in the first argument to this method. The second argument is optional, and names the schema in which the table exists. The keys of the associative array returned are the column names of the table. The value corresponding to each column is also an associative array, with the following keys and values: Metadata fields returned by describeTable() Key Type Description SCHEMA_NAME (string) Name of the database schema in which this table exists. TABLE_NAME (string) Name of the table to which this column belongs. COLUMN_NAME (string) Name of the column. COLUMN_POSITION (integer) Ordinal position of the column in the table. DATA_TYPE (string) RDBMS name of the datatype of the column. DEFAULT (string) Default value for the column, if any. NULLABLE (boolean) True if the column accepts SQL NULLs, false if the column has a NOT NULL constraint. LENGTH (integer) Length or size of the column as reported by the RDBMS. SCALE (integer) Scale of SQL NUMERIC or DECIMAL type. PRECISION (integer) Precision of SQL NUMERIC or DECIMAL type. UNSIGNED (boolean) True if an integer-based type is reported as UNSIGNED. PRIMARY (boolean) True if the column is part of the primary key of this table. PRIMARY_POSITION (integer) Ordinal position (1-based) of the column in the primary key. IDENTITY (boolean) True if the column uses an auto-generated value.
How the IDENTITY metadata field relates to specific RDBMS The IDENTITY metadata field was chosen as an 'idiomatic' term to represent a relation to surrogate keys. This field can be commonly known by the following values:- IDENTITY - DB2, MSSQL AUTO_INCREMENT - MySQL SERIAL - PostgreSQL SEQUENCE - Oracle If no table exists matching the table name and optional schema name specified, then describeTable() returns an empty array.
Closing a Connection Normally it is not necessary to close a database connection. PHP automatically cleans up all resources and the end of a request. Database extensions are designed to close the connection as the reference to the resource object is cleaned up. However, if you have a long-duration PHP script that initiates many database connections, you might need to close the connection, to avoid exhausting the capacity of your RDBMS server. You can use the Adapter's closeConnection() method to explicitly close the underlying database connection. Closing a database connection closeConnection(); ]]> Does Zend_Db support persistent connections? The usage of persistent connections is not supported or encouraged in Zend_Db. Using persistent connections can cause an excess of idle connections on the RDBMS server, which causes more problems than any performance gain you might achieve by reducing the overhead of making connections. Database connections have state. That is, some objects in the RDBMS server exist in session scope. Examples are locks, user variables, temporary tables, and information about the most recently executed query, such as rows affected, and last generated id value. If you use persistent connections, your application could access invalid or privileged data that were created in a previous PHP request. Running Other Database Statements There might be cases in which you need to access the connection object directly, as provided by the PHP database extension. Some of these extensions may offer features that are not surfaced by methods of Zend_Db_Adapter_Abstract. For example, all SQL statements run by Zend_Db are prepared, then executed. However, some database features are incompatible with prepared statements. DDL statements like CREATE and ALTER cannot be prepared in MySQL. Also, SQL statements don't benefit from the MySQL Query Cache , prior to MySQL 5.1.17. Most PHP database extensions provide a method to execute SQL statements without preparing them. For example, in PDO, this method is exec() . You can access the connection object in the PHP extension directly using getConnection(). Running a non-prepared statement in a PDO adapter getConnection()->exec('DROP TABLE bugs'); ]]> Similarly, you can access other methods or properties that are specific to PHP database extensions. Be aware, though, that by doing this you might constrain your application to the interface provided by the extension for a specific brand of RDBMS. In future versions of Zend_Db, there will be opportunities to add method entry points for functionality that is common to the supported PHP database extensions. This will not affect backward compatibility. Notes on Specific Adapters This section lists differences between the Adapter classes of which you should be aware. IBM DB2 Specify this Adapter to the factory() method with the name 'Db2'. This Adapter uses the PHP extension ibm_db2. IBM DB2 supports both sequences and auto-incrementing keys. Therefore the arguments to lastInsertId() are optional. If you give no arguments, the Adapter returns the last value generated for an auto-increment key. If you give arguments, the Adapter returns the last value generated by the sequence named according to the convention ' table _ column _seq'. MySQLi Specify this Adapter to the factory() method with the name 'Mysqli'. This Adapter utilizes the PHP extension mysqli. MySQL does not support sequences, so lastInsertId() ignores its arguments and always returns the last value generated for an auto-increment key. The lastSequenceId() method returns null . Oracle Specify this Adapter to the factory() method with the name 'Oracle'. This Adapter uses the PHP extension oci8. Oracle does not support auto-incrementing keys, so you should specify the name of a sequence to lastInsertId() or lastSequenceId() . The Oracle extension does not support positional parameters. You must use named parameters. Currently the Zend_Db::CASE_FOLDING option is not supported by the Oracle adapter. To use this option with Oracle, you must use the PDO OCI adapter. PDO for IBM DB2 and Informix Dynamic Server (IDS) Specify this Adapter to the factory() method with the name 'Pdo_Ibm'. This Adapter uses the PHP extensions pdo and pdo_ibm. You must use at least PDO_IBM extension version 1.2.2. If you have an earlier version of this extension, you must upgrade the PDO_IBM extension from PECL. PDO Microsoft SQL Server Specify this Adapter to the factory() method with the name 'Pdo_Mssql'. This Adapter uses the PHP extensions pdo and pdo_mssql. Microsoft SQL Server does not support sequences, so lastInsertId() ignores its arguments and always returns the last value generated for an auto-increment key. The lastSequenceId() method returns null . If you are working with unicode strings in an encoding other than UCS-2 (such as UTF-8), you may have to perform a conversion in your application code or store the data in a binary column. Please refer to Microsoft's Knowledge Base for more information. Zend_Db_Adapter_Pdo_Mssql sets QUOTED_IDENTIFIER ON immediately after connecting to a SQL Server database. This makes the driver use the standard SQL identifier delimiter symbol ( " ) instead of the proprietary square-brackets syntax SQL Server uses for delimiting identifiers. You can specify pdoType as a key in the options array. The value can be "mssql" (the default), "dblib", "freetds", or "sybase". This option affects the DSN prefix the adapter uses when constructing the DSN string. Both "freetds" and "sybase" imply a prefix of "sybase:", which is used for the FreeTDS set of libraries. See also http://www.php.net/manual/en/ref.pdo-dblib.connection.php for more information on the DSN prefixes used in this driver. PDO MySQL Specify this Adapter to the factory() method with the name 'Pdo_Mysql'. This Adapter uses the PHP extensions pdo and pdo_mysql. MySQL does not support sequences, so lastInsertId() ignores its arguments and always returns the last value generated for an auto-increment key. The lastSequenceId() method returns null . PDO Oracle Specify this Adapter to the factory() method with the name 'Pdo_Oci'. This Adapter uses the PHP extensions pdo and pdo_oci. Oracle does not support auto-incrementing keys, so you should specify the name of a sequence to lastInsertId() or lastSequenceId() . PDO PostgreSQL Specify this Adapter to the factory() method with the name 'Pdo_Pgsql'. This Adapter uses the PHP extensions pdo and pdo_pgsql. PostgreSQL supports both sequences and auto-incrementing keys. Therefore the arguments to lastInsertId() are optional. If you give no arguments, the Adapter returns the last value generated for an auto-increment key. If you give arguments, the Adapter returns the last value generated by the sequence named according to the convention ' table _ column _seq'. PDO SQLite Specify this Adapter to the factory() method with the name 'Pdo_Sqlite'. This Adapter uses the PHP extensions pdo and pdo_sqlite. SQLite does not support sequences, so lastInsertId() ignores its arguments and always returns the last value generated for an auto-increment key. The lastSequenceId() method returns null . To connect to an SQLite2 database, specify 'sqlite2'=>true in the array of parameters when creating an instance of the Pdo_Sqlite Adapter. To connect to an in-memory SQLite database, specify 'dbname'=>':memory:' in the array of parameters when creating an instance of the Pdo_Sqlite Adapter. Older versions of the SQLite driver for PHP do not seem to support the PRAGMA commands necessary to ensure that short column names are used in result sets. If you have problems that your result sets are returned with keys of the form "tablename.columnname" when you do a join query, then you should upgrade to the current version of PHP. Firebird/Interbase This Adapter uses the PHP extension php_interbase. Firebird/interbase does not support auto-incrementing keys, so you should specify the name of a sequence to lastInsertId() or lastSequenceId() . Currently the Zend_Db::CASE_FOLDING option is not supported by the Firebird/interbase adapter. Unquoted identifiers are automatically returned in upper case.
Zend_Db_Statement Además de algunos métodos convenientes tales como fetchAll() e insert() documentados en , puede usarse un objeto de declaración para obtener más opciones al ejecutar consultas y devolver conjuntos de resultados. Esta sección describe cómo obtener una instancia de un objeto de declaración y como usar sus métodos. Zend_Db_Statement está basado en el objeto PDOStatement en la extensión PHP Data Objects. Creando una Declaración Típicamente, un objeto de declaración statement es devuelto por el método query() de la clase de Adaptador de la base de datos. Este método es un modo general de preparar una declaración SQL. El primer parámetro es un string conteniendo la declaración SQL. El segundo parámetro (opcional) es un array de valores para vincular posiciones de parámetros en el string SQL. Crear un objeto de declaración SQL con query() query( 'SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?', array('goofy', 'FIXED') ); ]]> El objeto de declaración corresponde a una declaración SQL que ha sido preparada, y ejecutada una vez con valores vinculados especificados. Si la declaración fue una consulta SELECT u otro tipo de declaración que devuelve un conjunto de resultados, ahora estará lista para extraer resultados. Puede crear una declaración con su constructor, pero este es un uso menos típico. No hay un método factory para crear el objeto, así que es necesario cargar una clase de declaración específica y llamar a su constructor. Pase el objeto Adaptador como el primer parámetro, y un string conteniendo la declaración SQL como el segundo parámetro. La declaración es preparada pero no ejecutada. Usando un constructor de declaración SQL Ejecutando la declaración Necesita ejecutar un objeto de declaración si lo crea con el constructor, o si desea ejecutar la misma declaración varias veces. Use el método execute() del mismo objeto de declaración. El único parámetro es un array de valores a vincular a posiciones de parámetros en la declaración. Si usa parámetros posicionales, o los que están marcados por un signo de interrogación (?), pase los valores de vinculación en un array plano. Ejecutar una declaración con parámetros posicionales execute(array('goofy', 'FIXED')); ]]> Si usa parámetros nombrados, o los que son indicados por un string identificador precedido por un caracter de dos puntos (:), pase el valor en un array asociativo. Las claves de este array deben coincidir con el nombre de los parámetros. Ejecutando una declaración con parámetros nombrados execute(array(':reporter' => 'goofy', ':status' => 'FIXED')); ]]> Las declaraciones PDO soportan tanto parámetros posicionales como parámetros nombrados, pero no ambos tipos en la misma declaración SQL. Algunas clases Zend_Db_Statement para extensiones no-PDO soportan solo un tipo de parámetro o el otro. Extrayendo Resultados de una declaración <code>SELECT</code> Puede llamar a métodos del objeto de declaración para obtener filas desde declaraciones SQL que producen conjuntos de resultados. SELECT, SHOW, DESCRIBE y EXPLAIN son ejemplos de declaraciones que producen un conjunto de resultados. INSERT, UPDATE, and DELETE son ejemplo de declaraciones que no producen un conjunto de resultados. Puede ejecutar las últimas declaraciones de SQL usando Zend_Db_Statement, pero no puede llamar a los métodos que extraen filas de resultados desde este. Extrayendo una Fila Simple desde un Conjunto de Resultados Para extraer una fila desde el conjunto de resultados, use el método fetch() del objeto de declaración. Los tres parámetros de este método son opcionales: Estilo de Extracción es el primer parámetro. Éste controla la estructura en la que será devuelta la fila. Vea para la descripción de un valor válido los correspondientes formatos de datos. Orientación del Cursor es el segundo parámetro. Por omisión es Zend_Db::FETCH_ORI_NEXT, lo cual simplemente significa que cada llamada a fetch() devuelve la siguiente fila del resultado, en el orden devuelto por el RDBMS. Compensación es el tercer parámetro. Si la orientación del cursor es Zend_Db::FETCH_ORI_ABS, entonces el offset es el número ordinal de las filas que devolver. Si la orientación del cursor es Zend_Db::FETCH_ORI_REL, entonces el offset es relativo a la posición del cursor antes de que fetch() fuera llamado. fetch() devuelve false si todas las filas del conjunto de resultados han sido extraídas. Usando fetch() en un bucle query('SELECT * FROM bugs'); while ($row = $stmt->fetch()) { echo $row['bug_description']; } ]]> Vea también PDOStatement::fetch(). Extrayendo un Conjunto de Resultados completo Para extraer todas las filas de un resultado en un solo paso, use el método fetchAll(). Esto es equivalente a llamar al método fetch() en un bucle devolviendo todas las filas en una array. El método fetchAll() acepta 2 parámetros. El primero es el estilo de extracción, descrito anteriormente, y el segundo indica el número de la columa que devolver, cuando el estilo de extracción es Zend_Db::FETCH_COLUMN. Usando fetchAll() query('SELECT * FROM bugs'); $rows = $stmt->fetchAll(); echo $rows[0]['bug_description']; ]]> Vea también PDOStatement::fetchAll(). Cambiando el Modo de extracción Por defecto, el objeto de declaración devuelve filas de un conjunto de resultados como array asociativo, mapeando los nombres de columnas a los valores de la columna. Se puede especificar un formato diferente para que la clase de declaración devuelva las filas, tal como se puede con la clase Adaptadora. Puede usar él método setFetchMode() para establecer el modo de extracción. Especifique el modo de extracción usando las constantes de la clase Zend_Db: FETCH_ASSOC, FETCH_NUM, FETCH_BOTH, FETCH_COLUMN, and FETCH_OBJ. Vea para más información de estos modos. Llamadas subsiguientes a los métodos de la declaración fetch() o fetchAll() usan el modo de extracción especificado. Configurando un modo de extracción query('SELECT * FROM bugs'); $stmt->setFetchMode(Zend_Db::FETCH_NUM); $rows = $stmt->fetchAll(); echo $rows[0][0]; ]]> Vea también PDOStatement::setFetchMode(). Extrayendo una Única Columna desde un Conjunto de Resultados Para devolver una única columna de la siguiente fila del conjunto de resultados, use fetchColumn(). El parámetro opcional es el índice de la columna (integer), y por defecto es 0. Este método devuelve un valor escalar, o false si todas las filas del conjunto de resultados han sido extraídas. Note que este método opera diferente que el método fetchCol() de la clase Adaptadora. El método fetchColumn() de una declaración devuelve un único valor desde una fila. El método fetchCol() de un adaptador devuelve un array de valores, tomados desde la primera columa de todas las del conjunto de resultados. Usando fetchColumn() query('SELECT bug_id, bug_description, bug_status FROM bugs'); $bug_status = $stmt->fetchColumn(2); ]]> Vea también PDOStatement::fetchColumn(). Extrayendo una Fila como un Objeto Para extraer una fila desde un conjunto de resultados estructurado como un Objeto, use el método fetchObject(). Este método tiene 2 parámetros opcionales. El primer parámetro es un string con el nombre de la clase del objeto que devolver; por defecto será 'stdClass'. El segundo parámetro es un array de valores que serán pasados al constructor de la clase. Usando fetchObject() query('SELECT bug_id, bug_description, bug_status FROM bugs'); $obj = $stmt->fetchObject(); echo $obj->bug_description; ]]> Vea también PDOStatement::fetchObject(). Zend_Db_Profiler Introducción Zend_Db_Profiler puede ser habilitado para Perfilar las consultas. Los Perfiles incluyen la consulta procesada por el adaptador como el tiempo as transcurrido en la ejecución de las consultas, permitiendo inspeccionar las consultas realizadas win necesidad de agregar información de depuración extra en el código de las clases. El uso avanzado también permite que el desarrollador filtre las consultas que desea perfilar. Habilite el perfilador pasando una directiva al al constructor del adaptador, o pidiendole al adaptador permitirlo más adelante. '127.0.0.1', 'username' => 'webuser', 'password' => 'xxxxxxxx', 'dbname' => 'test' 'profiler' => true // enciende el perfilador // establezca false para deshabilitar (está deshabilitado por defecto) ); $db = Zend_Db::factory('PDO_MYSQL', $params); // apagar el perfilador: $db->getProfiler()->setEnabled(false); // encender el perfilador: $db->getProfiler()->setEnabled(true); ]]> El valor de la opción 'profiler' es flexible. Es interpretada de distintas formas dependiendo del tipo. Normalmente, debería usar un valor booleano simple, pero otros tipos le permiten personalizar el comportamiento del perfilador. Un argumento booleano establece el perfilador como habilitado si el valor es true, o deshabilitado si es false. La clase del perfilador es el la clase de perfilador por defecto del adaptador, Zend_Db_Profiler. Una instancia del objeto perfilador hace que el adaptador use ese objeto. El tipo del objeto debe ser Zend_Db_Profiler o una subclase de este. Habilitar el perfilador se hace por separado. setEnabled(true); $params['profiler'] = $profiler; $db = Zend_Db::factory('PDO_MYSQL', $params); ]]> El argumento puede ser un array asociativo conteniendo algunas o todas las claves 'enabled', 'instance', y 'class'. Las claves 'enabled' e 'instance' corresponden a los tipos booleano y la instancia documentada previamente. La clave 'class' es usada para nombrar la clase que usará el perfilador personalizado. La clase debe ser Zend_Db_Profiler o una subclase. La clase es instanciada sin argumentos de constructor. La opción 'class' es ignorada cuando la opción 'instance' está dada. true, 'class' => 'MyProject_Db_Profiler' ); $db = Zend_Db::factory('PDO_MYSQL', $params); ]]> Finalmente, el argumento puede ser un objeto de tipo Zend_Config conteniendo las propiedades, que son tratadas como las claves de array descritas recién. Por ejemplo, un archivo "config.ini" puede contener los siguientes datos: Esta configuración puede ser aplicada con el siguiente código PHP: db->profiler; $db = Zend_Db::factory('PDO_MYSQL', $params); ]]> La propiedad 'instance' debe ser usada como el siguiente ejemplo: setEnabled(true); $configData = array( 'instance' => $profiler ); $config = new Zend_Config($configData); $params['profiler'] = $config; $db = Zend_Db::factory('PDO_MYSQL', $params); ]]> Usando el Perfilador En este punto, obtenemos el perfilador usando el método getProfiler() del adaptador: getProfiler(); ]]> Este retorna una instancia del objeto Zend_Db_Profiler. Con esta instancia, el desarrollador puede examinar las consultar usando una variedad de métodos: getTotalNumQueries() retorna el número total de consultas que han sido perfiladas. getTotalElapsedSecs() retorna el número total de segundos transcurridos en todas las consultas perfiladas. getQueryProfiles() retorna un array con todos los perfiles de consultas. getLastQueryProfile() retorna el último perfil (más reciente) de consulta, independientemente de si la consulta ha terminado o no (si no lo ha hecho, la hora de finalización será nula). clear() limpia los perfiles de consulta de la pila. El valor de retorno de getLastQueryProfile() y elementos individuales de getQueryProfiles() son Zend_Db_Profiler_Query objetos, que proporcionan la capacidad para inspeccionar cada una de las consultas: getQuery() retorna el texto SQL de la consulta. El texto SQL de una declaración preparada con parámetros es el texto al tiempo en que la consulta fué preparada, por lo que contiene marcadores de posición, no los valores utilizados cuando la declaración se ejecuta. getQueryParams() retorna un array de los valores de los parámetros usados cuando se ejecuta una consulta preparada. Este incluye ambos parámetros y argumentos vinculados al método execute() de la declaración. Las claves del array son las posiciones (basado en 1) o indices de parámetros nombrados (string). getElapsedSecs() returna el número de segundos que tuvo la consulta al correr. La información que Zend_Db_Profiler provee es útil para perfilar cuellos de botella en aplicaciones, y para depurar consultas que han sido ejecutadas. Por instancia, para ver la consulta exacta que tuvo la última ejecución: getLastQueryProfile(); echo $query->getQuery(); ]]> Tal vez una página se genera lentamente; use el perfilador para determinar primero el número total de segundos de todas las consultas, y luego recorrer paso a paso a través de las consultas para encontrar la más lenta: getTotalElapsedSecs(); $queryCount = $profiler->getTotalNumQueries(); $longestTime = 0; $longestQuery = null; foreach ($profiler->getQueryProfiles() as $query) { if ($query->getElapsedSecs() > $longestTime) { $longestTime = $query->getElapsedSecs(); $longestQuery = $query->getQuery(); } } echo 'Ejecutadas ' . $queryCount . ' consultas en ' . $totalTime . ' segundos' . "\n"; echo 'Promedio de tiempo de consulta: ' . $totalTime / $queryCount . ' segundos' . "\n"; echo 'Consultas por segundo: ' . $queryCount / $totalTime . "\n"; echo 'Tardanza de la consulta más lenta: ' . $longestTime . "\n"; echo "Consulta más lenta: \n" . $longestQuery . "\n"; ]]> Uso avanzado del Perfilador Además de la inspección de consultas, el perfilador también le permite al desarrollador filtrar que consultas serán perfiladas. El siguiente método opera en una instancia de Zend_Db_Profiler: Filtrar por tiempo transcurrido en consulta setFilterElapsedSecs() le permite al desarrolador establecer un tiempo mínimo antes de que una consulta se perfile. Para remover el filtro, pase un valor null al método. setFilterElapsedSecs(5); // Perfilar todas las consultas sin importar el tiempo: $profiler->setFilterElapsedSecs(null); ]]> Filtrar por tipo de consulta setFilterQueryType() le permite al desarrollador establecer que tipo de consulta serán perfiladas; para perfilar multiples tipos, use un "OR" lógico. Los tipos de consulta se definen como las siguientes constantes de Zend_Db_Profiler: Zend_Db_Profiler::CONNECT: operaciones de conexión o selección de base de datos. Zend_Db_Profiler::QUERY: consultas generales a la base de datos que no calzan con otros tipos. Zend_Db_Profiler::INSERT: cualquier consulta que agrega filas a la base de datos, generalmente un SQL INSERT. Zend_Db_Profiler::UPDATE: cualquier consulta que actualice registros existentes, usualmente un SQL UPDATE. Zend_Db_Profiler::DELETE: cualquier consulta que elimine datos existentes, usualmente un SQL DELETE. Zend_Db_Profiler::SELECT: cualquier consulta que retorne datos existentes, usualmente un SQL SELECT. Zend_Db_Profiler::TRANSACTION: cualquier operación transaccional, tal como iniciar una transacción, confirmar, o revertir. Asi como con setFilterElapsedSecs(), puedes remover cualquier filtro existente pasando un null como único argumento. setFilterQueryType(Zend_Db_Profiler::SELECT); // Perfila consultas SELECT, INSERT, y UPDATE $profiler->setFilterQueryType(Zend_Db_Profiler::SELECT | Zend_Db_Profiler::INSERT | Zend_Db_Profiler::UPDATE); // Perfilar consultas DELETE $profiler->setFilterQueryType(Zend_Db_Profiler::DELETE); // Remover todos los filtros $profiler->setFilterQueryType(null); ]]> Obtener perfiles por tipo de consulta Usando setFilterQueryType() puedes reducir los perfiles generados. Sin embargo, a veces puede ser más útil mantener todos los perfiles, pero ver sólo los que necesita en un determinado momento. Otra característica de getQueryProfiles() es que puede este filtrado al-vuelo, pasando un tipo de consulta(o una combinación lógica de tipos de consulta) en el primer; vea para una lista las constantes de tipo de consulta. getQueryProfiles(Zend_Db_Profiler::SELECT); // Obtiene los perfiles de consultas SELECT, INSERT, y UPDATE $profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::SELECT | Zend_Db_Profiler::INSERT | Zend_Db_Profiler::UPDATE); // Obtiene solo perfiles de consultas DELETE $profiles = $profiler->getQueryProfiles(Zend_Db_Profiler::DELETE); ]]> Perfiladores Especializados Un Perfilador Especializado es un objeto que hereda de Zend_Db_Profiler. Los Perfiladores Especializados tratan la información de perfilado de maneras más especificas. Perfilando con Firebug Zend_Db_Profiler_Firebug envía información de perfilado a la Consola de Firebug. Todos los datos son enviados a través del componente Zend_Wildfire_Channel_HttpHeaders que usa cabeceras HTTP para asegurar que el contenido de la página no sea alterado. Depurar peticiones AJAX que requieren respuestas JSON y XML es perfectamente posible con este enfoque. Requerimientos: Navegador web Firefox idealmente versión 3, pero la versión 2 tambien está soportada. Extensión Firebug para Firefox, la cual puede descargarse desde https://addons. mozilla .org/en-US/firefox/addon/1843. Extensión FirePHP para Firefox, la cual puede descargarse desde https://addons.mozilla.org/en-US/firefox/addon/6149. Perfilando DB con <code>Zend_Controller_Front</code> setEnabled(true); // Anexar el perfilador a tu adaptador de base de datos $db->setProfiler($profiler) // Despachar el controlador frontal // Todas las consultas a la base de datos en tus archivos modelo, vista y controlador // ahora serán perfilados y enviados a Firebug ]]> Perfilar DB sin <code>Zend_Controller_Front</code> setEnabled(true); // Anexar el perfilador a tu adaptador de base de datos $db->setProfiler($profiler) $request = new Zend_Controller_Request_Http(); $response = new Zend_Controller_Response_Http(); $channel = Zend_Wildfire_Channel_HttpHeaders::getInstance(); $channel->setRequest($request); $channel->setResponse($response); // Iniciar un buffer de las salidas ob_start(); // Ahora se pueden ejecutar las consultas a la Base de Datos para ser perfiladas // Enviar los datos de perfilado al navegador $channel->flush(); $response->sendHeaders(); ]]> Zend_Db_Select Descripción del Objeto Select El objeto Zend_Db_Select object representa una declaración de consulta SELECT de SQL. La clase tiene métodos para agregar partes individuales a la consulta. Puedes especificar algunas partes de la consulta usando los métodos en PHP y sus estructuras de datos, y la clase forma la sintaxis SLQ correcta. Despues de construir la consulta, puedes ejecutarla como si se hubiera escrito como un string. El valor entregado por Zend_Db_Select incluye: Métodos Orientados a objetos para especificar consultas SQL de manera pieza-a-pieza; Abstracción de partes de las consultas SQL, independiente de la Base de datos; Entrecomillado automático de identificadores de metadatos en la mayoría de los casos, soportanto identificadores que contienen palabras reservadas de SQL y caracteres especiales; Entrecomillado de identificadores y valores, para ayudar a reducir el riesgo de ataque de inyección SQL. El uso de Zend_Db_Select no es obligatorio. Para consultas SELECT muy simples, es usualmente más simple especificar la consulta completa como un string y ejecutarla usando un método del Adapter como query() o fetchAll(). Usar Zend_Db_Select es util si se necesita ensamblar una consulta SELECT proceduralmente, o basado en condiciones lógicas en la aplicación. Creando un Objeto Select Puedes crear un a instancia del objeto Zend_Db_Select usando el método select() de un objeto Zend_Db_Adapter_Abstract. Ejemplo del método select() del adaptador select(); ]]> Otra manera de crear el objeto Zend_Db_Select es con su constructor, especificando el adaptador de base de datos como un argumento. Ejemplo de creación de un nuevo objeto Select Construyendo consultas Select Cuando se construye una consulta, puede agregar clausulas a esta, una por una. Hay un método separado para agregar cada al objeto Zend_Db_Select. Ejemplo de uso de métodos que agregan cláusulas select(); // Agregar una cláusula FROM $select->from( ...specify table and columns... ) // Agregar una cláusula WHERE $select->where( ...specify search criteria... ) // Agregar una cláusula ORDER BY $select->order( ...specify sorting criteria... ); ]]> También puede utilizar la mayoría de los métodos del objeto Zend_Db_Select con una interfaz fluida. Una interfaz fluida significa que cada método retorna una referencia al objeto que se ha llamado, así puedes llamar inmediatamente a otro método. Ejemplo de uso de la interfaz fluida. select() ->from( ...specify table and columns... ) ->where( ...specify search criteria... ) ->order( ...specify sorting criteria... ); ]]> Los ejemplos en esta sección muestran el uso de la interfaz fluída, pero también puedes usar la interfaz no-fluída en todos los casos. A menudo es necesario utilizar la interfaz no-fluída, por ejemplo, si su aplicación necesita realizar cierta lógica antes de añadir una cláusula a la consulta. Agregando una cláusula FROM Especifica la tabla para esta consulta usando el método from(). Puedes especificar el nombre de la tabla como un simple string. Zend_Db_Select aplica el identificador entrecomillando el nombre de la tabla, así puedes usar caracteres especiales. Ejemplo del método from() select() ->from( 'products' ); ]]> Puedes especificar un nombre correlacionado (también llamado a veces "alias de tabla") para una tabla. En lugar de un simple string, se usa un array asociativo que mapee el nombre de la correlación con el nombre de la tabla. En otras cláusulas de consulta SQL, se usa esta correlación de nombre. si su consulta se une con más de una tabla, Zend_Db_Select generatiza una correlación unica de nombres basados en el nombre de la tabla, para una tabla a la cual no se le espicifique un nombre correlacionado. Ejemplo especificando una tabla con nombre correlacionado select() ->from( array('p' => 'products') ); ]]> Algunos RDBMS apoyan el uso de un especificador de esquema para una tabla. Puedes especificar el nombre de la tabla como "nombreDeEsquema.nombre DeTabla", donde Zend_Db_Select entrecomillará cada parte individualmente, o tambien puedes especificar el nombre de esquema por separado. Un nombre de esquema especificado en el nombre de la tabla toma precedencia en sobre un esquema dado por separado en el caso de que ambos sean dados. Ejemplo especificando un nombre de esquema select() ->from( 'myschema.products' ); // o $select = $db->select() ->from('products', '*', 'myschema'); ]]> Agregando Columnas En el segundo argumento del método from(), puedes especificar las columnas a seleccionar desde la respectiva tabla. Si no especificas columns, por defecto será "*", el comodín SQL para "todas las columnas". Puedes listar las columnas en un simple array de strings, o en un array asociativo mapeando los alias de columnas a su nombre de tabla. Si solo se especifica una columna en la consulta y no necesitas especificar un alias de columna, puedes listarla solo con un string plano de lugar de un array. Si se entrega un array vacío como el argumento de las tablas, no se incluirán columnas en el resultado. Vea un codigo de ejemplo bajo la sección del método join(). Puedes especificar el nombre de columna como "nombreCorrelacionado.nombreDeColumna". Zend_Db_Select entrecomullará cada parte individualmente. Si no especificas un nombre correlacionado para una columna, se usará el nombre correlacionado para la tabla nombrada en el actual método from(). Ejemplos especificando columnas select() ->from(array('p' => 'products'), array('product_id', 'product_name')); // Construir la misma consulta, especificando nombres correlacionados: // SELECT p."product_id", p."product_name" // FROM "products" AS p $select = $db->select() ->from(array('p' => 'products'), array('p.product_id', 'p.product_name')); // Construir esta consulta con una alias para una columna: // SELECT p."product_id" AS prodno, p."product_name" // FROM "products" AS p $select = $db->select() ->from(array('p' => 'products'), array('prodno' => 'product_id', 'product_name')); ]]> Agregando una Expresión en las Columns Las columnas en consultas SQL a veces son expresiones, no simples columnas de una tabla. Las expresiones no deberían tener nombres correlacionados o entrecomillado aplicado. Si sus columnas contienen parentesis, Zend_Db_Select las reconoce como una expresión. Tambien puedes crear un objeto de tipo Zend_Db_Expr explícitamente, para prevenir que el string sea tratado como columna. Zend_Db_Expr es una clase mínima, que contiene un simple string. Zend_Db_Select reconoce el objeto de tipo Zend_Db_Expr y lo convierte de vuelta en el string, pero no le aplica ninguna alteración, tal como el entrecomillado o la correlación de nombres. El Uso de Zend_Db_Expr para nombres de columnas no es necesario si la expresión de la columna contiene parentesis; Zend_Db_Select reconoce y trata el string como expresión, saltándose el entrcomillado y la correlación de nombres. Ejemplos especificando columnas que contienen expresiones select() ->from(array('p' => 'products'), array('product_id', 'LOWER(product_name)')); // Construye esta consulta: // SELECT p."product_id", (p.cost * 1.08) AS cost_plus_tax // FROM "products" AS p $select = $db->select() ->from(array('p' => 'products'), array('product_id', 'cost_plus_tax' => '(p.cost * 1.08)') ); // Construye esta consulta usando Zend_Db_Expr explícitamente: // SELECT p."product_id", p.cost * 1.08 AS cost_plus_tax // FROM "products" AS p $select = $db->select() ->from(array('p' => 'products'), array('product_id', 'cost_plus_tax' => new Zend_Db_Expr('p.cost * 1.08')) ); ]]> En los casos anteriores, Zend_Db_Select no altera el string para aplicar correlación de nombres o entrecomillado de identificadores. Si estos cambios son necesarios para resolver ambigüedades, deberías realizar cambios manualmente en el string. Si el nombre de su columna es alguna palabra reservada de SQL o contiene caracteres especiales, debería usar el método quoteIdentifier() del Adapdator e interpolar el resultado en un string. El método quoteIdentifier() usa entrecomillado SQL para delimitar el identificador, the identifier, dejando en claro que es un identificador de tabla o columna y no otra parte de la sintaxis SQL. Su código es más independiente de la base de datos si se usa el método quoteIdentifier() en vez de las excribir literalmente las comillas en la cadena, debido a que algunos RDBMS no usan simbolos estándar para entrecomillar identificadores. El método quoteIdentifier() está diseñado para usar los símbolos apropiados para entrecomillar basado en el tipo del adaptador. El método quoteIdentifier() también escapa cual caracter de comilla que aparezca en el nombre del identificador mismo. Ejemplo de entrecomillado de columnas en una expresión select() ->from(array('p' => 'products'), array('origin' => '(p.' . $db->quoteIdentifier('from') . ' + 10)') ); ]]> Agregar columnas a una tabla FROM o JOIN existente Puede haber casos en los que desea agregar columnas a una tabla FROM o JOIN después de que estos métodos han sido llamados. El método columns() permite agregar columnas en cualquier punto antes de ejecutar la consulta. Puedes pasar las columnas bien como un string, un Zend_Db_Expr o un array de estos elementos. El segundo argumento para este método puede ser omitido, implicando que las columnas serán agregadas a una tabla FROM, en otro caso debería usarse un nombre de correlación existente. Ejemplos agregando columnas con el método<code>columns()</code> select() ->from(array('p' => 'products'), 'product_id') ->columns('product_name'); // Construir la misma consulta, especificando correlación de nombres: // SELECT p."product_id", p."product_name" // FROM "products" AS p $select = $db->select() ->from(array('p' => 'products'), 'p.product_id') ->columns('product_name', 'p'); // Alternativamente puede usar columns('p.product_name')]]> Agregar Otra Tabla a la Consulta Query con JOIN Muchas consultas útiles involucran el uso de un JOIN para combinar filas de multiples tablas. Puedes agregar tablas a una consulta Zend_Db_Select usando el método join(). Usar este método, es similar al método from(), excepto que puedes especificar una condición de unión en la mayoría de los casos. Ejemplo del método join() select() ->from(array('p' => 'products'), array('product_id', 'product_name')) ->join(array('l' => 'line_items'), 'p.product_id = l.product_id'); ]]> El segundo argumento join() es un string que es usado como condición de unión. Esta es una expresión que declara un criterio por el cual las filas en una tabla concuerdan con las filas de la otra tabla. Puedes especificar correlación de nombres en esta expresión. No se aplica entrecomillado en la expresión especificada para la condición de unión; si tienes problemas con nombres que necesitan ser entrecomillados, deberás usar quoteIdentifier() para formar el string de condición de unión. El tercer argumento join() es un array de nombres de columnas, como al usar el método from(). Este es por defecto "*", soporta correlación de nombres, expresiones, y Zend_Db_Expr de la misma manera que el array de nombres de columnas en el método from(). Para no seleccionar columnas de una tabla, use un array vacío para la lista de columnas. El uso de esto trabaja con el método from() también, pero en general deseará algunas columnas de la tabla primaria en sus consultas, a la vez que no se desean columnas de la tabla unida. Ejemplo especificando ninguna columna select() ->from(array('p' => 'products'), array('product_id', 'product_name')) ->join(array('l' => 'line_items'), 'p.product_id = l.product_id', array() ); // empty list of columns ]]> Note el array vacío array() en el ejemplo anterior en lugar de una lista de columnas de la tabla unida. SQL tiene muchos tipos de uniones. Vea una lista a continuación para los métodos que soportan cada tipo de unión en Zend_Db_Select. INNER JOIN con los métodos join(table, join, [columns]) o joinInner(table, join, [columns]). Esta es el tipo de unión más comun. Las filas de cada tabla son comparadas usando la condición de unión especificada. El resultado incluye solo las filas que satisfacen la condición. El resultado puede ser vacio si no hay filas que satisfagan la condición. Todos los RDBMS soportan este tipo de unión. LEFT JOIN con el método joinLeft(table, condition, [columns]). Todas las filas de tabla a la izquierda del operando son incluídas, pareando las filas de la tabla a la derecha del operando, y las columnas de la tabla a la derecha del operando son rellenadas con NULLs si no existen filas que calcen con la tabla a la izquierda. Todos los RDBMS soportan este tipo de unión. RIGHT JOIN con el método joinRight(table, condition, [columns]). Unión exterior por la derecha es un complemento de la unión exterior por la izquierda. Todas las filas de la tabla a la derecha del operando son incluídos, pareando las filas de la tabla a la izquierda del operando incluídos, y las columnas de la tabla a la izquierda del operando son rellenadas con NULLs si no existen filas que calcen con la tabla de la derecha. Algunos RDBMS no soportan'este tipo de join, pero en general, cualquier unión por la derecha puede representarse por una unión por la derecha invirtiendo el orden de las tablas. FULL JOIN con el método joinFull(table, condition, [columns]). Una unión externa total es como una combinación de una unión exterior por la izquierda y una unión exterior por la derecha. Todas las filas de ambas tablas son incluídas, vinculadas entre sí en la misma fila si estos satisfacen la condición de unión, y en otro caso se vinculan con valores nulos en lugar de columnas de la otra tabla. Algunos RDBMS no soportan este tipo de unión. CROSS JOIN con el método joinCross(table, [columns]). Una unión cruzada es un Producto Cartesiano. Cada fila en la primera tabla es pareada con cada una en la segunda tabla. Por lo tanto, el número de filas en el resultado es igual al producto del número de filas en cada tabla. Puede filtrar el conjunto de resultados con el uso de condiciones en una cláusula WHERE; de esta forma una unión cruzada es similar a la antigua sintaxis de unión en SQL-89. El método joinCross() no tiene parámetros para especificar una condición de unión. Algunos RDBMS no soportan este tipo de unión. NATURAL JOIN con el método joinNatural(table, [columns]). Una unión natural compara cualquier columa(s) que aparezca con el nombre en ambas tablas. La comparación es el equivalente de todas las columna(s); comparando las columnas usando desigualdad no es una unión natural. Solo la unión interna natural es soportada por este API, aun cuando SQL permita una unión externa natural. El método joinNatural() no tiene parámetros para especificar una condición. Además de los métodos de unión, puedes simplificar las consultas usando métodos JoinUsing. En vez de proveer una condición completa a la unión, simplemente pasas el nombre de columna en la que se hará la uninón y el objeto Zend_Db_Select completa la condición por ti. Ejemplo de método joinUsing() select() ->from('table1') ->joinUsing('table2', 'column1') ->where('column2 = ?', 'foo');]]> Cada uno de los métodos aplicables para uniones en el componente Zend_Db_Select tiene su correspondiente método 'usando'. joinUsing(table, join, [columns]) y joinInnerUsing(table, join, [columns]) joinLeftUsing(table, join, [columns]) joinRightUsing(table, join, [columns]) joinFullUsing(table, join, [columns]) Agregar una cláusula WHERE Puede especificar un criterio para restringir las filas de resultado usando el método where(). El primer argumento de este método es una expresión SQL, y esta expresión es usada como una expresión SQL WHERE en la consulta. Ejemplo del método where() 100.00 $select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where('price > 100.00');]]> No se aplica entrecomillado en una expresión dada en el método where() u orWhere(). Si tienes nombres de columnas que necesitan ser entrecomillada, debe usar el método quoteIdentifier() para formar el string de la condición. El segundo argumento del método where() es opcional. Es un valor a sustituir en la expresión. Zend_Db_Select entrecomilla el valor y sustituye por un signo de interrogación ("?") en la expresión. Este método acepta solo un parámetro. Si tienes una expresión en la cual necesitas sustituir multiples variables, deberás formar el string manualmente, interpolando variables y realizando entrecomillado tu mismo. Ejemplo de parámetro en el método where() 100.00) $minimumPrice = 100; $select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where('price > ?', $minimumPrice); ]]> Puedes invocar el método where() multiples veces en el mismo objeto Zend_Db_Select. La consulta resultante combina los multiples terminos juntos usando AND entre ellos. Ejemplo de multiples métodos where() 100.00) // AND (price < 500.00) $minimumPrice = 100; $maximumPrice = 500; $select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where('price > ?', $minimumPrice) ->where('price < ?', $maximumPrice); ]]> Si necesitas combinar terminos juntos uando OR, use el método orWhere(). Este mñetodo se usa del mismo modo que el método where(), excepto que el termino especificado es precedido por OR, en lugar de AND. Ejemplo del método orWhere() 500.00) $minimumPrice = 100; $maximumPrice = 500; $select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where('price < ?', $minimumPrice) ->orWhere('price > ?', $maximumPrice); ]]> Zend_Db_Select automáticamente pone paréntesis alrededor de cada expresión que especifiques usandp el método where() u orWhere(). Esto ayuda a asegurar que la precedencia del operador Booleano no cause resultados inesperados. Ejemplos de Expresiones Booleanas con parentesis 500.00) // AND (product_name = 'Apple') $minimumPrice = 100; $maximumPrice = 500; $prod = 'Apple'; $select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where("price < $minimumPrice OR price > $maximumPrice") ->where('product_name = ?', $prod); ]]> En el ejemplo anterior, los resultados deberían ser diferentes sin paréntesis, porque AND tiene alta precedencia respecto a OR. Zend_Db_Select aplica el parentesis con un efecto tal que la expresión en sucesivas llamadas al método where() vincule más estrechamente el AND que combina las expresiones. Agregando una cláusula GROUP BY En SQL, la cláusula GROUP BY permite reducir el número de filas del resultado de una consulta a una fila por cada valor único encontrado en la(s) columna(s) nombrada(s) en la cláusula GROUP BY. En Zend_Db_Select, puedes especificar la(s) columna(s) a usar para el cálculo de grupos de filas usando el método group(). El argumento de este método es una columna o un array de columnas que se usarán en la cláusula GROUP BY. Ejemplo del método groups group() select() ->from(array('p' => 'products'), array('product_id')) ->join(array('l' => 'line_items'), 'p.product_id = l.product_id', array('line_items_per_product' => 'COUNT(*)')) ->group('p.product_id'); ]]> Como el array de columnas del método from(), peudes usar correlación de nombres en el string de nombre de columna, y la conlumna será entrecomillada como un identificador, salvo que el string contenga paréntesis o sea un objeto de tipo Zend_Db_Expr. Agregando una cláusula HAVING En SQL, la cláusula HAVING aplica una condición de restricción en grupos de filas. Es similar a una cláusula WHERE aplicando una condición de restricción a las filas. Pero las 2 cláusulas son diferentes porque las condiciones WHERE son aplicadas antes que definan los grupos, mientras que las condiciones HAVING son aplicadas después que los grupos son definidos. En Zend_Db_Select, puedes especificar condiciones para restringir grupos usando el método having(). Su uso es similar al del método where(). El primer agumento es un string conteniendo una expresión SQL. El segundo argumento es un valor que es usado para reemplazar un parámetro marcador de posición en la expresión SQL. Las expresiones dadas en multiples invocaciones al método having() son combinados usando el operador Booleano AND, o el operador OR si usas el método orHaving(). Ejemplo del método having() 10 $select = $db->select() ->from(array('p' => 'products'), array('product_id')) ->join(array('l' => 'line_items'), 'p.product_id = l.product_id', array('line_items_per_product' => 'COUNT(*)')) ->group('p.product_id') ->having('line_items_per_product > 10'); ]]> No se aplica entrecomillado a expresiones dadas al método having() u orHaving(). Si tienes nombres de columnas que deban ser entrecomillados, deberás usar quoteIdentifier() para formar el string de la condición. Agregar una cláusula ORDER BY En SQL, la cláusula ORDER BY especifica una o más columnas o expresiones por el cual el resultado de la consulta será ordenado. Si multiples columnas son listadas, las columnas secundarias serán usadas para resolver relaciones; el orden de clasificación es determinado por columnas secundarias si la columna anterior contiene valores identicos. El orden por defecto es del menor valor al mayor valor. Puedes también ordenar de mayor a menor valor para una columna dada en la lista espeificando la palabra clave DESC despues de la columna. En Zend_Db_Select, puedes usar el método el método order() para especificar una columna o un array de columnas por el cual ordenar. Cada elemento del array es un string nombrando la columna. Opcionalmente con la palabra reservada ASC o DESC siguiendola, deparada por un espacio. Como en el método from() y group(), los nombres de columnas son entrecomillados como identificadores, a menos que contengan paréntesis o sean un obheto de tipo Zend_Db_Expr. Ejemplo del método order() select() ->from(array('p' => 'products'), array('product_id')) ->join(array('l' => 'line_items'), 'p.product_id = l.product_id', array('line_items_per_product' => 'COUNT(*)')) ->group('p.product_id') ->order(array('line_items_per_product DESC', 'product_id')); ]]> Agregando una cláusula LIMIT Algunos RDBMS extienden una consulta SQL con una cláusula conocida como LIMIT. Esta cláusuala reduce el número de filas en el resultado a no más de un número especificado. También puedes especificar saltar el número de filas antes de empezar la salida. Esta característica hace más fácil tomar un subconjunto de resultados, por ejemplo cuando mostramos los resultados de una consulta en progresivas páginas de salida. En Zend_Db_Select, puedes usar el método limit() para especificar la cantidad de filas y el número de filas a saltar. El primer argumento es el método es el número de filas deseado. El segundo argument es el númerp de filas a saltar. Ejemplo del método limit() select() ->from(array('p' => 'products'), array('product_id', 'product_name')) ->limit(10, 20); ]]> La sintaxis de LIMIT no está soportada por todos los RDBMS brands. Algunos RDBMS requieren diferente sintaxis para soportar una funcionalidad simialr. Cada clase Zend_Db_Adapter_Abstract incluye un método para producir el SQL apropiado para cada RDBMS. Use el método limitPage() como un modo alternativa de especificar la cantidad de filas y compensación. Este método permite limitar el conjunto resultado a una serie de subconjuntos de tamaño fijo de filas del total del resultado de la consulta. En otras palabras, puedes especificar el tamaño de una "página" de resultados, y el número ordinal de la página simple donde se espera que retorne la consulta. El número de página es el primer argumento del método limitPage(), y la longitud de la página es el segundo argumento. Ambos son argumentos requeridos; no tienen valores por omisión. Ejemplo del método limitPage() select() ->from(array('p' => 'products'), array('product_id', 'product_name')) ->limitPage(2, 10); ]]> Agregar el modificador DISTINCT a la consulta El método distinct() permite agregar la palabra clave a la consulta DISTINCT a su consulta SQL. Ejemplo del método distinct() select() ->distinct() ->from(array('p' => 'products'), 'product_name'); ]]> Agregar el modificador FOR UPDATE El método forUpdate() permite agregar el modificador FOR UPDATE a su consulta SQL. Example of forUpdate() method select() ->forUpdate() ->from(array('p' => 'products')); ]]> Ejecutando consultas Select Esta sección se describe como ejecutar una consulta representada por un objeto Zend_Db_Select. Ejecutando Consultas SelectExecuting desde el Adaptador de Base de Datos Puedes ejecutar la consulta representada por el objeto Zend_Db_Select pasandolo como primer argumento al método query() de un objeto Zend_Db_Adapter_Abstract. Use objetos Zend_Db_Select en lugar de un string de consulta. El método query() retorna un objeto de tipo Zend_Db_Statement o PDOStatement, dependiendo del tipo de adaptador. Ejemplo usando el método adaptador query() del Adaptador de Base de datos select() ->from('products'); $stmt = $db->query($select); $result = $stmt->fetchAll(); ]]> Ejecutando Consultas Select desde el Objeto Como alternativa al uso del método query() del objeto adaptador, puedes usar el método query() del objeto Zend_Db_Select. Ambos métodos retornan un objeto de tipo Zend_Db_Statement o PDOStatement, dependiendo del tipo de adaptador. Ejempo usando el método query() del objeto Select select() ->from('products'); $stmt = $select->query(); $result = $stmt->fetchAll(); ]]> Convertiendo un Objeto Select a un String SQL Si necesitas acceder a una represantación en un string de la consulta SQL correspondiente al objeto Zend_Db_Select, use el método __toString(). Ejemplo del método __toString() select() ->from('products'); $sql = $select->__toString(); echo "$sql\n"; // The output is the string: // SELECT * FROM "products" ]]> Otros Métodos Esta sección describe otros métodos de Zend_Db_Select que no han sido cubiertos antes: getPart() y reset(). Obtener Partes de un Objeto Select El método getPart() retorna una representación de una parte de su consulta SQL. Por ejemplo, puedes usar este método para retornar un array de expresiones para la cláusula WHERE, o el array de columnas (o expresiones de columnas) que estan en la lista del SELECT, o los valores de la cantidad y comienzo para la cláusula LIMIT. El valor de retorno no es un string conteniendo un fragmento de la sintaxis SQL. El valor de retorno es una representación, típicamente un array con una estructura que contiene valores y expresiones. Cada parte de la consulta tiene una estructura diferente. El único argumento del método getPart() es un string que identifica que parte del la consulta Select va a retornar. Por ejemplo, el string 'from' identifica la parte del objeto Select que almacena la información de las tablas de la cláusula FROM, incluyendo uniones de tablas. La clase Zend_Db_Select define constantes que puedes usar para las partes de la consulta SQL. Puedes usar estas definiciones de constantes, o los strings literales. Constantes usedas por getPart() y reset() Constante Valor del String Zend_Db_Select::DISTINCT 'distinct' Zend_Db_Select::FOR_UPDATE 'forupdate' Zend_Db_Select::COLUMNS 'columns' Zend_Db_Select::FROM 'from' Zend_Db_Select::WHERE 'where' Zend_Db_Select::GROUP 'group' Zend_Db_Select::HAVING 'having' Zend_Db_Select::ORDER 'order' Zend_Db_Select::LIMIT_COUNT 'limitcount' Zend_Db_Select::LIMIT_OFFSET 'limitoffset'
Ejemplo del método getPart() select() ->from('products') ->order('product_id'); // Puedes especificar un string string literal para especificar la parte $orderData = $select->getPart( 'order' ); // Puedes usar una constante para especificar la misma parte $orderData = $select->getPart( Zend_Db_Select::ORDER ); // El valor de retorno puede ser una estructura en un array, no un string. // Cada parte tiene distinta estructura. print_r( $orderData ); ]]>
Restableciendo Partes de un Objeto El método reset() permite limpiar una parte específica de la consulta SQL, o limpia todas las partes de la consulta SQL si omites el argumento. El argumento es opcional. Puedes especificar la parte de la consulta que será limpiada, usando los mismos strings que usa el argumento del método getPart(). La parte de la consulta que especifiques se reestablecerá a su estado por omisión. Si omites el parámetro, reset() cambia todas las partes de la consulta a su estado por omisión. Esto hace que el objeto Zend_Db_Select sea equivalente a crear un nuevo objeto, como si acabases de instanciarlo. Ejemplo del método reset() select() ->from(array('p' => 'products') ->order('product_name'); // Requisito cambiado, en su lugar un orden diferente de columnas: // SELECT p.* // FROM "products" AS p // ORDER BY "product_id" // Limpia una parte para poder redefinirla $select->reset( Zend_Db_Select::ORDER ); // Y especificar una columna diferente $select->order('product_id'); // Limpia todas las partes de la consulta $select->reset(); ]]>
Zend_Db_Table Introduction to Table Class The Zend_Db_Table class is an object-oriented interface to database tables. It provides methods for many common operations on tables. The base class is extensible, so you can add custom logic. The Zend_Db_Table solution is an implementation of the Table Data Gateway pattern. The solution also includes a class that implements the Row Data Gateway pattern. Defining a Table Class For each table in your database that you want to access, define a class that extends Zend_Db_Table_Abstract. Defining the Table Name and Schema Declare the database table for which this class is defined, using the protected variable $_name. This is a string, and must contain the name of the table spelled as it appears in the database. Declaring a table class with explicit table name If you don't specify the table name, it defaults to the name of the class. If you rely on this default, the class name must match the spelling of the table name as it appears in the database. Declaring a table class with implicit table name You can also declare the schema for the table, either with the protected variable $_schema, or with the schema prepended to the table name in the $_name property. Any schema specified with the $_name property takes precedence over a schema specified with the $_schema property. In some RDBMS brands, the term for schema is "database" or "tablespace," but it is used similarly. Declaring a table class with schema The schema and table names may also be specified via constructor configuration directives, which override any default values specified with the $_name and $_schema properties. A schema specification given with the name directive overrides any value provided with the schema option. Declaring table and schema names upon instantiation 'bugs', 'schema' => 'bug_db')); // Second alternative: $tableBugs = new Bugs(array('name' => 'bug_db.bugs'); // If schemas are specified in both 'name' and 'schema', the one // specified in 'name' takes precedence: $tableBugs = new Bugs(array('name' => 'bug_db.bugs', 'schema' => 'ignored'); ]]> If you don't specify the schema name, it defaults to the schema to which your database adapter instance is connected. Defining the Table Primary Key Every table must have a primary key. You can declare the column for the primary key using the protected variable $_primary. This is either a string that names the single column for the primary key, or else it is an array of column names if your primary key is a compound key. Example of specifying the primary key If you don't specify the primary key, Zend_Db_Table_Abstract tries to discover the primary key based on the information provided by the describeTable()´ method. Every table class must know which column(s) can be used to address rows uniquely. If no primary key column(s) are specified in the table class definition or the table constructor arguments, or discovered in the table metadata provided by describeTable(), then the table cannot be used with Zend_Db_Table. Overriding Table Setup Methods When you create an instance of a Table class, the constructor calls a set of protected methods that initialize metadata for the table. You can extend any of these methods to define metadata explicitly. Remember to call the method of the same name in the parent class at the end of your method. Example of overriding the _setupTableName() method _name = 'bugs'; parent::_setupTableName(); } } ]]> The setup methods you can override are the following: _setupDatabaseAdapter() checks that an adapter has been provided; gets a default adapter from the registry if needed. By overriding this method, you can set a database adapter from some other source. _setupTableName() defaults the table name to the name of the class. By overriding this method, you can set the table name before this default behavior runs. _setupMetadata() sets the schema if the table name contains the pattern "schema.table"; calls describeTable() to get metadata information; defaults the $_cols array to the columns reported by describeTable(). By overriding this method, you can specify the columns. _setupPrimaryKey() defaults the primary key columns to those reported by describeTable(); checks that the primary key columns are included in the $_cols array. By overriding this method, you can specify the primary key columns. Table initialization If application-specific logic needs to be initialized when a Table class is constructed, you can select to move your tasks to the init() method, which is called after all Table metadata has been processed. This is recommended over the __construct method if you do not need to alter the metadata in any programmatic way. Example usage of init() method _observer = new MyObserverClass(); } } ]]> Creating an Instance of a Table Before you use a Table class, create an instance using its constructor. The constructor's argument is an array of options. The most important option to a Table constructor is the database adapter instance, representing a live connection to an RDBMS. There are three ways of specifying the database adapter to a Table class, and these three ways are described below: Specifying a Database Adapter The first way to provide a database adapter to a Table class is by passing it as an object of type Zend_Db_Adapter_Abstract in the options array, identified by the key 'db'. Example of constructing a Table using an Adapter object $db)); ]]> Setting a Default Database Adapter The second way to provide a database adapter to a Table class is by declaring an object of type Zend_Db_Adapter_Abstract to be a default database adapter for all subsequent instances of Tables in your application. You can do this with the static method Zend_Db_Table_Abstract::setDefaultAdapter(). The argument is an object of type Zend_Db_Adapter_Abstract. Example of constructing a Table using a the Default Adapter It can be convenient to create the database adapter object in a central place of your application, such as the bootstrap, and then store it as the default adapter. This gives you a means to ensure that the adapter instance is the same throughout your application. However, setting a default adapter is limited to a single adapter instance. Storing a Database Adapter in the Registry The third way to provide a database adapter to a Table class is by passing a string in the options array, also identified by the 'db' key. The string is used as a key to the static Zend_Registry instance, where the entry at that key is an object of type Zend_Db_Adapter_Abstract. Example of constructing a Table using a Registry key 'my_db')); ]]> Like setting the default adapter, this gives you the means to ensure that the same adapter instance is used throughout your application. Using the registry is more flexible, because you can store more than one adapter instance. A given adapter instance is specific to a certain RDBMS brand and database instance. If your application needs access to multiple databases or even multiple database brands, then you need to use multiple adapters. Inserting Rows to a Table You can use the Table object to insert rows into the database table on which the Table object is based. Use the insert() method of your Table object. The argument is an associative array, mapping column names to values. Example of inserting to a Table '2007-03-22', 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); $table->insert($data); ]]> By default, the values in your data array are inserted as literal values, using parameters. If you need them to be treated as SQL expressions, you must make sure they are distinct from plain strings. Use an object of type Zend_Db_Expr to do this. Example of inserting expressions to a Table new Zend_Db_Expr('CURDATE()'), 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); ]]> In the examples of inserting rows above, it is assumed that the table has an auto-incrementing primary key. This is the default behavior of Zend_Db_Table_Abstract, but there are other types of primary keys as well. The following sections describe how to support different types of primary keys. Using a Table with an Auto-incrementing Key An auto-incrementing primary key generates a unique integer value for you if you omit the primary key column from your SQL INSERT statement. In Zend_Db_Table_Abstract, if you define the protected variable $_sequence to be the Boolean value true, then the class assumes that the table has an auto-incrementing primary key. Example of declaring a Table with auto-incrementing primary key MySQL, Microsoft SQL Server, and SQLite are examples of RDBMS brands that support auto-incrementing primary keys. PostgreSQL has a SERIAL notation that implicitly defines a sequence based on the table and column name, and uses the sequence to generate key values for new rows. IBM DB2 has an IDENTITY notation that works similarly. If you use either of these notations, treat your Zend_Db_Table class as having an auto-incrementing column with respect to declaring the $_sequence member as true. Using a Table with a Sequence A sequence is a database object that generates a unique value, which can be used as a primary key value in one or more tables of the database. If you define $_sequence to be a string, then Zend_Db_Table_Abstract assumes the string to name a sequence object in the database. The sequence is invoked to generate a new value, and this value is used in the INSERT operation. Example of declaring a Table with a sequence Oracle, PostgreSQL, and IBM DB2 are examples of RDBMS brands that support sequence objects in the database. PostgreSQL and IBM DB2 also have syntax that defines sequences implicitly and associated with columns. If you use this notation, treat the table as having an auto-incrementing key column. Define the sequence name as a string only in cases where you would invoke the sequence explicitly to get the next key value. Using a Table with a Natural Key Some tables have a natural key. This means that the key is not automatically generated by the table or by a sequence. You must specify the value for the primary key in this case. If you define the $_sequence to be the Boolean value false, then Zend_Db_Table_Abstract assumes that the table has a natural primary key. You must provide values for the primary key columns in the array of data to the insert() method, or else this method throws a Zend_Db_Table_Exception. Example of declaring a Table with a natural key All RDBMS brands support tables with natural keys. Examples of tables that are often declared as having natural keys are lookup tables, intersection tables in many-to-many relationships, or most tables with compound primary keys. Updating Rows in a Table You can update rows in a database table using the update method of a Table class. This method takes two arguments: an associative array of columns to change and new values to assign to these columns; and an SQL expression that is used in a WHERE clause, as criteria for the rows to change in the UPDATE operation. Example of updating rows in a Table '2007-03-23', 'bug_status' => 'FIXED' ); $where = $table->getAdapter()->quoteInto('bug_id = ?', 1234); $table->update($data, $where); ]]> Since the table update() method proxies to the database adapter update() method, the second argument can be an array of SQL expressions. The expressions are combined as Boolean terms using an AND operator. The values and identifiers in the SQL expression are not quoted for you. If you have values or identifiers that require quoting, you are responsible for doing this. Use the quote(), quoteInto(), and quoteIdentifier() methods of the database adapter. Deleting Rows from a Table You can delete rows from a database table using the delete() method. This method takes one argument, which is an SQL expression that is used in a WHERE clause, as criteria for the rows to delete. Example of deleting rows from a Table getAdapter()->quoteInto('bug_id = ?', 1235); $table->delete($where); ]]> The second argument can be an array of SQL expressions. The expressions are combined as Boolean terms using an AND operator. Since the table delete() method proxies to the database adapter delete() method, the second argument can be an array of SQL expressions. The expressions are combined as Boolean terms using an AND operator. The values and identifiers in the SQL expression are not quoted for you. If you have values or identifiers that require quoting, you are responsible for doing this. Use the quote(), quoteInto(), and quoteIdentifier() methods of the database adapter. Finding Rows by Primary Key You can query the database table for rows matching specific values in the primary key, using the find() method. The first argument of this method is either a single value or an array of values to match against the primary key of the table. Example of finding rows by primary key values find(1234); // Find multiple rows // Also returns a Rowset $rows = $table->find(array(1234, 5678)); ]]> If you specify a single value, the method returns at most one row, because a primary key cannot have duplicate values and there is at most one row in the database table matching the value you specify. If you specify multiple values in an array, the method returns at most as many rows as the number of distinct values you specify. The find() method might return fewer rows than the number of values you specify for the primary key, if some of the values don't match any rows in the database table. The method even may return zero rows. Because the number of rows returned is variable, the find() method returns an object of type Zend_Db_Table_Rowset_Abstract. If the primary key is a compound key, that is, it consists of multiple columns, you can specify the additional columns as additional arguments to the find() method. You must provide as many arguments as the number of columns in the table's primary key. To find multiple rows from a table with a compound primary key, provide an array for each of the arguments. All of these arrays must have the same number of elements. The values in each array are formed into tuples in order; for example, the first element in all the array arguments define the first compound primary key value, then the second elements of all the arrays define the second compound primary key value, and so on. Example of finding rows by compound primary key values The call to find() below to match multiple rows can match two rows in the database. The first row must have primary key value (1234, 'ABC'), and the second row must have primary key value (5678, 'DEF'). find(1234, 'ABC'); // Find multiple rows with compound primary keys // Also returns a Rowset $rows = $table->find(array(1234, 5678), array('ABC', 'DEF')); ]]> Querying for a Set of Rows Select API The API for fetch operations has been superseded to allow a Zend_Db_Table_Select object to modify the query. However, the deprecated usage of the fetchRow() and fetchAll() methods will continue to work without modification. The following statements are all legal and functionally identical, however it is recommended to update your code to take advantage of the new usage where possible. fetchAll('bug_status = "NEW"', 'bug_id ASC', 10, 0); $rows = $table->fetchAll($table->select()->where('bug_status = ?', 'NEW') ->order('bug_id ASC') ->limit(10, 0)); // Fetching a single row $row = $table->fetchRow('bug_status = "NEW"', 'bug_id ASC'); $row = $table->fetchRow($table->select()->where('bug_status = ?', 'NEW') ->order('bug_id ASC')); ]]> The Zend_Db_Table_Select object is an extension of the Zend_Db_Select object that applies specific restrictions to a query. The enhancements and restrictions are: You can elect to return a subset of columns within a fetchRow or fetchAll query. This can provide optimization benefits where returning a large set of results for all columns is not desirable. You can specify columns that evaluate expressions from within the selected table. However this will mean that the returned row or rowset will be readOnly and cannot be used for save() operations. A Zend_Db_Table_Row with readOnly status will throw an exception if a save() operation is attempted. You can allow JOIN clauses on a select to allow multi-table lookups. You can not specify columns from a JOINed tabled to be returned in a row/rowset. Doing so will trigger a PHP error. This was done to ensure the integrity of the Zend_Db_Table is retained. i.e. A Zend_Db_Table_Row should only reference columns derived from its parent table. Simple usage select(); $select->where('bug_status = ?', 'NEW'); $rows = $table->fetchAll($select); ]]> Fluent interfaces are implemented across the component, so this can be rewritten this in a more abbreviated form. Example of fluent interface fetchAll($table->select()->where('bug_status = ?', 'NEW')); ]]> Fetching a rowset You can query for a set of rows using any criteria other than the primary key values, using the fetchAll() method of the Table class. This method returns an object of type Zend_Db_Table_Rowset_Abstract. Example of finding rows by an expression select()->where('bug_status = ?', 'NEW'); $rows = $table->fetchAll($select); ]]> You may also pass sorting criteria in an ORDER BY clause, as well as count and offset integer values, used to make the query return a specific subset of rows. These values are used in a LIMIT clause, or in equivalent logic for RDBMS brands that do not support the LIMIT syntax. Example of finding rows by an expression select()->where(array('bug_status = ?' => 'NEW')) ->order($order) ->limit($count, $offset); $rows = $table->fetchAll($select); ]]> All of the arguments above are optional. If you omit the ORDER clause, the result set includes rows from the table in an unpredictable order. If no LIMIT clause is set, you retrieve every row in the table that matches the WHERE clause. Advanced usage For more specific and optimized requests, you may wish to limit the number of columns returned in a row/rowset. This can be achieved by passing a FROM clause to the select object. The first argument in the FROM clause is identical to that of a Zend_Db_Select object with the addition of being able to pass an instance of Zend_Db_Table_Abstract and have it automatically determine the table name. Retrieving specific columns select(); $select->from($table, array('bug_id', 'bug_description')) ->where('bug_status = ?', 'NEW'); $rows = $table->fetchAll($select); ]]> The rowset contains rows that are still 'valid' - they simply contain a subset of the columns of a table. If a save() method is called on a partial row then only the fields available will be modified. You can also specify expressions within a FROM clause and have these returned as a readOnly row/rowset. In this example we will return a rows from the bugs table that show an aggregate of the number of new bugs reported by individuals. Note the GROUP clause. The 'count' column will be made available to the row for evaluation and can be accessed as if it were part of the schema. Retrieving expressions as columns select(); $select->from($table, array('COUNT(reported_by) as `count`', 'reported_by')) ->where('bug_status = ?', 'NEW') ->group('reported_by'); $rows = $table->fetchAll($select); ]]> You can also use a lookup as part of your query to further refine your fetch operations. In this example the accounts table is queried as part of a search for all new bugs reported by 'Bob'. Using a lookup table to refine the results of fetchAll() select(); $select->where('bug_status = ?', 'NEW') ->join('accounts', 'accounts.account_name = bugs.reported_by') ->where('accounts.account_name = ?', 'Bob'); $rows = $table->fetchAll($select); ]]> The Zend_Db_Table_Select is primarily used to constrain and validate so that it may enforce the criteria for a legal SELECT query. However there may be certain cases where you require the flexibility of the Zend_Db_Table_Row component and do not require a writable or deletable row. For this specific user case, it is possible to retrieve a row/rowset by passing a false value to setIntegrityCheck. The resulting row/rowset will be returned as a 'locked' row (meaning the save(), delete() and any field-setting methods will throw an exception). Removing the integrity check on Zend_Db_Table_Select to allow JOINed rows select()->setIntegrityCheck(false); $select->where('bug_status = ?', 'NEW') ->join('accounts', 'accounts.account_name = bugs.reported_by', 'account_name') ->where('accounts.account_name = ?', 'Bob'); $rows = $table->fetchAll($select); ]]> Querying for a Single Row You can query for a single row using criteria similar to that of the fetchAll() method. Example of finding a single row by an expression select()->where('bug_status = ?', 'NEW') ->order('bug_id'); $row = $table->fetchRow($select); ]]> This method returns an object of type Zend_Db_Table_Row_Abstract. If the search criteria you specified match no rows in the database table, then fetchRow() returns PHP's null value. Retrieving Table Metadata Information The Zend_Db_Table_Abstract class provides some information about its metadata. The info() method returns an array structure with information about the table, its columns and primary key, and other metadata. Example of getting the table name info(); echo "The table name is " . $info['name'] . "\n"; ]]> The keys of the array returned by the info() method are described below: name => the name of the table. cols => an array, naming the column(s) of the table. primary => an array, naming the column(s) in the primary key. metadata => an associative array, mapping column names to information about the columns. This is the information returned by the describeTable() method. rowClass => the name of the concrete class used for Row objects returned by methods of this table instance. This defaults to Zend_Db_Table_Row. rowsetClass => the name of the concrete class used for Rowset objects returned by methods of this table instance. This defaults to Zend_Db_Table_Rowset. referenceMap => an associative array, with information about references from this table to any parent tables. See . dependentTables => an array of class names of tables that reference this table. See . schema => the name of the schema (or database or tablespace) for this table. Caching Table Metadata By default, Zend_Db_Table_Abstract queries the underlying database for table metadata whenever that data is needed to perform table operations. The table object fetches the table metadata from the database using the adapter's describeTable() method. Operations requiring this introspection include: insert() find() info() In some circumstances, particularly when many table objects are instantiated against the same database table, querying the database for the table metadata for each instance may be undesirable from a performance standpoint. In such cases, users may benefit by caching the table metadata retrieved from the database. There are two primary ways in which a user may take advantage of table metadata caching: Call Zend_Db_Table_Abstract::setDefaultMetadataCache() - This allows a developer to once set the default cache object to be used for all table classes. Configure Zend_Db_Table_Abstract::__construct() - This allows a developer to set the cache object to be used for a particular table class instance. In both cases, the cache specification must be either null (i.e., no cache used) or an instance of Zend_Cache_Core. The methods may be used in conjunction when it is desirable to have both a default metadata cache and the ability to change the cache for individual table objects. Using a Default Metadata Cache for all Table Objects The following code demonstrates how to set a default metadata cache to be used for all table objects: true ); $backendOptions = array( 'cache_dir' => 'cacheDir' ); $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions); // Next, set the cache to be used with all table objects Zend_Db_Table_Abstract::setDefaultMetadataCache($cache); // A table class is also needed class Bugs extends Zend_Db_Table_Abstract { // ... } // Each instance of Bugs now uses the default metadata cache $bugs = new Bugs(); ]]> Using a Metadata Cache for a Specific Table Object The following code demonstrates how to set a metadata cache for a specific table object instance: true ); $backendOptions = array( 'cache_dir' => 'cacheDir' ); $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions); // A table class is also needed class Bugs extends Zend_Db_Table_Abstract { // ... } // Configure an instance upon instantiation $bugs = new Bugs(array('metadataCache' => $cache)); ]]> Automatic Serialization with the Cache Frontend Since the information returned from the adapter's describeTable() method is an array, ensure that the automatic_serialization option is set to true for the Zend_Cache_Core frontend. Though the above examples use Zend_Cache_Backend_File, developers may use whatever cache backend is appropriate for the situation. Please see Zend_Cache for more information. Hardcoding Table Metadata To take metadata caching a step further, you can also choose to hardcode metadata. In this particular case, however, any changes to the table schema will require a change in your code. As such, it is only recommended for those who are optimizing for production usage. The metadata structure is as follows: ' => array( 'SCHEMA_NAME' => , 'TABLE_NAME' => , 'COLUMN_NAME' => , 'COLUMN_POSITION' => , 'DATA_TYPE' => , 'DEFAULT' => NULL|, 'NULLABLE' => , 'LENGTH' => , 'SCALE' => NULL|, 'PRECISION' => NULL|, 'UNSIGNED' => NULL|, 'PRIMARY' => , 'PRIMARY_POSITION' => , 'IDENTITY' => , ), // additional columns... ); ]]> An easy way to get the appropriate values is to use the metadata cache, and then to deserialize values stored in the cache. You can disable this optimization by turning of the metadataCacheInClass flag: false)); // Or later: $bugs->setMetadataCacheInClass(false); ]]> The flag is enabled by default, which ensures that the $_metadata array is only populated once per instance. Customizing and Extending a Table Class Using Custom Row or Rowset Classes By default, methods of the Table class return a Rowset in instances of the concrete class Zend_Db_Table_Rowset, and Rowsets contain a collection of instances of the concrete class Zend_Db_Table_Row. You can specify an alternative class to use for either of these, but they must be classes that extend Zend_Db_Table_Rowset_Abstract and Zend_Db_Table_Row_Abstract, respectively. You can specify Row and Rowset classes using the Table constructor's options array, in keys 'rowClass' and 'rowsetClass' respectively. Specify the names of the classes using strings. Example of specifying the Row and Rowset classes 'My_Row', 'rowsetClass' => 'My_Rowset' ) ); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW') // Returns an object of type My_Rowset, // containing an array of objects of type My_Row. $rows = $table->fetchAll($where); ]]> You can change the classes by specifying them with the setRowClass() and setRowsetClass() methods. This applies to rows and rowsets created subsequently; it does not change the class of any row or rowset objects you have created previously. Example of changing the Row and Rowset classes getAdapter()->quoteInto('bug_status = ?', 'NEW') // Returns an object of type Zend_Db_Table_Rowset // containing an array of objects of type Zend_Db_Table_Row. $rowsStandard = $table->fetchAll($where); $table->setRowClass('My_Row'); $table->setRowsetClass('My_Rowset'); // Returns an object of type My_Rowset, // containing an array of objects of type My_Row. $rowsCustom = $table->fetchAll($where); // The $rowsStandard object still exists, and it is unchanged. ]]> For more information on the Row and Rowset classes, see and . Defining Custom Logic for Insert, Update, and Delete You can override the insert() and update() methods in your Table class. This gives you the opportunity to implement custom code that is executed before performing the database operation. Be sure to call the parent class method when you are done. Custom logic to manage timestamps You can also override the delete() method. Define Custom Search Methods in Zend_Db_Table You can implement custom query methods in your Table class, if you have frequent need to do queries against this table with specific criteria. Most queries can be written using fetchAll(), but this requires that you duplicate code to form the query conditions if you need to run the query in several places in your application. Therefore it can be convenient to implement a method in the Table class to perform frequently-used queries against this table. Custom method to find bugs by status getAdapter()->quoteInto('bug_status = ?', $status); return $this->fetchAll($where, 'bug_id'); } } ]]> Define Inflection in Zend_Db_Table Some people prefer that the table class name match a table name in the RDBMS by using a string transformation called inflection. For example, if your table class name is "BugsProducts", it would match the physical table in the database called "bugs_products," if you omit the explicit declaration of the $_name class property. In this inflection mapping, the class name spelled in "CamelCase" format would be transformed to lower case, and words are separated with an underscore. You can specify the database table name independently from the class name by declaring the table name with the $_name class property in each of your table classes. Zend_Db_Table_Abstract performs no inflection to map the class name to the table name. If you omit the declaration of $_name in your table class, the class maps to a database table that matches the spelling of the class name exactly. It is inappropriate to transform identifiers from the database, because this can lead to ambiguity or make some identifiers inaccessible. Using the SQL identifiers exactly as they appear in the database makes Zend_Db_Table_Abstract both simpler and more flexible. If you prefer to use inflection, then you must implement the transformation yourself, by overriding the _setupTableName() method in your Table classes. One way to do this is to define an abstract class that extends Zend_Db_Table_Abstract, and then the rest of your tables extend your new abstract class. Example of an abstract table class that implements inflection _name) { $this->_name = myCustomInflector(get_class($this)); } parent::_setupTableName(); } } class BugsProducts extends MyAbstractTable { } ]]> You are responsible for writing the functions to perform inflection transformation. Zend Framework does not provide such a function. Zend_Db_Table_Row Introduction Zend_Db_Table_Row is a class that contains an individual row of a Zend_Db_Table object. When you run a query against a Table class, the result is returned in a set of Zend_Db_Table_Row objects. You can also use this object to create new rows and add them to the database table. Zend_Db_Table_Row is an implementation of the Row Data Gateway pattern. Fetching a Row Zend_Db_Table_Abstract provides methods find() and fetchAll(), which each return an object of type Zend_Db_Table_Rowset, and the method fetchRow(), which returns an object of type Zend_Db_Table_Row. Example of fetching a row fetchRow($bugs->select()->where('bug_id = ?', 1)); ]]> A Zend_Db_Table_Rowset object contains a collection of Zend_Db_Table_Row objects. See . Example of reading a row in a rowset fetchAll($bugs->select()->where('bug_status = ?', 1)); $row = $rowset->current(); ]]> Reading column values from a row Zend_Db_Table_Row_Abstract provides accessor methods so you can reference columns in the row as object properties. Example of reading a column in a row fetchRow($bugs->select()->where('bug_id = ?', 1)); // Echo the value of the bug_description column echo $row->bug_description; ]]> Earlier versions of Zend_Db_Table_Row mapped these column accessors to the database column names using a string transformation called inflection. Currently, Zend_Db_Table_Row does not implement inflection. Accessed property names need to match the spelling of the column names as they appear in your database. Retrieving Row Data as an Array You can access the row's data as an array using the toArray() method of the Row object. This returns an associative array of the column names to the column values. Example of using the toArray() method fetchRow($bugs->select()->where('bug_id = ?', 1)); // Get the column/value associative array from the Row object $rowArray = $row->toArray(); // Now use it as a normal array foreach ($rowArray as $column => $value) { echo "Column: $column\n"; echo "Value: $value\n"; } ]]> The array returned from toArray() is not updateable. You can modify values in the array as you can with any array, but you cannot save changes to this array to the database directly. Fetching data from related tables The Zend_Db_Table_Row_Abstract class provides methods for fetching rows and rowsets from related tables. See for more information on table relationships. Writing rows to the database Changing column values in a row You can set individual column values using column accessors, similar to how the columns are read as object properties in the example above. Using a column accessor to set a value changes the column value of the row object in your application, but it does not commit the change to the database yet. You can do that with the save() method. Example of changing a column in a row fetchRow($bugs->select()->where('bug_id = ?', 1)); // Change the value of one or more columns $row->bug_status = 'FIXED'; // UPDATE the row in the database with new values $row->save(); ]]> Inserting a new row You can create a new row for a given table with the createRow() method of the table class. You can access fields of this row with the object-oriented interface, but the row is not stored in the database until you call the save() method. Example of creating a new row for a table createRow(); // Set column values as appropriate for your application $newRow->bug_description = '...description...'; $newRow->bug_status = 'NEW'; // INSERT the new row to the database $newRow->save(); ]]> The optional argument to the createRow() method is an associative array, with which you can populate fields of the new row. Example of populating a new row for a table '...description...', 'bug_status' => 'NEW' ); $bugs = new Bugs(); $newRow = $bugs->createRow($data); // INSERT the new row to the database $newRow->save(); ]]> The createRow() method was called fetchNew() in earlier releases of Zend_Db_Table. You are encouraged to use the new method name, even though the old name continues to work for the sake of backward compatibility. Changing values in multiple columns Zend_Db_Table_Row_Abstract provides the setFromArray() method to enable you to set several columns in a single row at once, specified in an associative array that maps the column names to values. You may find this method convenient for setting values both for new rows and for rows you need to update. Example of using setFromArray() to set values in a new Row createRow(); // Data are arranged in an associative array $data = array( 'bug_description' => '...description...', 'bug_status' => 'NEW' ); // Set all the column values at once $newRow->setFromArray($data); // INSERT the new row to the database $newRow->save(); ]]> Deleting a row You can call the delete() method on a Row object. This deletes rows in the database matching the primary key in the Row object. Example of deleting a row fetchRow('bug_id = 1'); // DELETE this row $row->delete(); ]]> You do not have to call save() to apply the delete; it is executed against the database immediately. Serializing and unserializing rows It is often convenient to save the contents of a database row to be used later. Serialization is the name for the operation that converts an object into a form that is easy to save in offline storage (for example, a file). Objects of type Zend_Db_Table_Row_Abstract are serializable. Serializing a Row Simply use PHP's serialize() function to create a string containing a byte-stream representation of the Row object argument. Example of serializing a row fetchRow('bug_id = 1'); // Convert object to serialized form $serializedRow = serialize($row); // Now you can write $serializedRow to a file, etc. ]]> Unserializing Row Data Use PHP's unserialize() function to restore a string containing a byte-stream representation of an object. The function returns the original object. Note that the Row object returned is in a disconnected state. You can read the Row object and its properties, but you cannot change values in the Row or execute other methods that require a database connection (for example, queries against related tables). Example of unserializing a serialized row bug_description; ]]> Why do Rows unserialize in a disconnected state? A serialized object is a string that is readable to anyone who possesses it. It could be a security risk to store parameters such as database account and password in plain, unencrypted text in the serialized string. You would not want to store such data to a text file that is not protected, or send it in an email or other medium that is easily read by potential attackers. The reader of the serialized object should not be able to use it to gain access to your database without knowing valid credentials. Reactivating a Row as Live Data You can reactivate a disconnected Row, using the setTable() method. The argument to this method is a valid object of type Zend_Db_Table_Abstract, which you create. Creating a Table object requires a live connection to the database, so by reassociating the Table with the Row, the Row gains access to the database. Subsequently, you can change values in the Row object and save the changes to the database. Example of reactivating a row setTable($bugs); // Now you can make changes to the row and save them $rowClone->bug_status = 'FIXED'; $rowClone->save(); ]]> Extending the Row class Zend_Db_Table_Row is the default concrete class that extends Zend_Db_Table_Row_Abstract. You can define your own concrete class for instances of Row by extending Zend_Db_Table_Row_Abstract. To use your new Row class to store results of Table queries, specify the custom Row class by name either in the $_rowClass protected member of a Table class, or in the array argument of the constructor of a Table object. Specifying a custom Row class 'MyRow')); ]]> Row initialization If application-specific logic needs to be initialized when a row is constructed, you can select to move your tasks to the init() method, which is called after all row metadata has been processed. This is recommended over the __construct method if you do not need to alter the metadata in any programmatic way. Example usage of init() method _role = new MyRoleClass(); } } ]]> Defining Custom Logic for Insert, Update, and Delete in Zend_Db_Table_Row The Row class calls protected methods _insert(), _update(), and _delete() before performing the corresponding operations INSERT, UPDATE, and DELETE. You can add logic to these methods in your custom Row subclass. If you need to do custom logic in a specific table, and the custom logic must occur for every operation on that table, it may make more sense to implement your custom code in the insert(), update() and delete() methods of your Table class. However, sometimes it may be necessary to do custom logic in the Row class. Below are some example cases where it might make sense to implement custom logic in a Row class instead of in the Table class: Example of custom logic in a Row class The custom logic may not apply in all cases of operations on the respective Table. You can provide custom logic on demand by implementing it in a Row class and creating an instance of the Table class with that custom Row class specified. Otherwise, the Table uses the default Row class. You need data operations on this table to record the operation to a Zend_Log object, but only if the application configuration has enabled this behavior. info(Zend_Debug::dump($this->_data, "INSERT: $this->_tableClass", false) ); } } // $loggingEnabled is an example property that depends // on your application configuration if ($loggingEnabled) { $bugs = new Bugs(array('rowClass' => 'MyLoggingRow')); } else { $bugs = new Bugs(); } ]]> Example of a Row class that logs insert data for multiple tables The custom logic may be common to multiple tables. Instead of implementing the same custom logic in every one of your Table classes, you can implement the code for such actions in the definition of a Row class, and use this Row in each of your Table classes. In this example, the logging code is identical in all table classes. info(Zend_Debug::dump($this->_data, "INSERT: $this->_tableClass", false) ); } } class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bugs'; protected $_rowClass = 'MyLoggingRow'; } class Products extends Zend_Db_Table_Abstract { protected $_name = 'products'; protected $_rowClass = 'MyLoggingRow'; } ]]> Define Inflection in Zend_Db_Table_Row Some people prefer that the table class name match a table name in the RDBMS by using a string transformation called inflection. Zend_Db classes do not implement inflection by default. See for an explanation of this policy. If you prefer to use inflection, then you must implement the transformation yourself, by overriding the _transformColumn() method in a custom Row class, and using that custom Row class when you perform queries against your Table class. Example of defining an inflection transformation This allows you to use an inflected version of the column name in the accessors. The Row class uses the _transformColumn() method to change the name you use to the native column name in the database table. fetchNew(); // Use camelcase column names, and rely on the // transformation function to change it into the // native representation. $row->bugDescription = 'New description'; ]]> You are responsible for writing the functions to perform inflection transformation. Zend Framework does not provide such a function. Zend_Db_Table_Rowset Introduction When you run a query against a Table class using the find() or fetchAll() methods, the result is returned in an object of type Zend_Db_Table_Rowset_Abstract. A Rowset contains a collection of objects descending from Zend_Db_Table_Row_Abstract. You can iterate through the Rowset and access individual Row objects, reading or modifying data in the Rows. Fetching a Rowset Zend_Db_Table_Abstract provides methods find() and fetchAll(), each of which returns an object of type Zend_Db_Table_Rowset_Abstract. Example of fetching a rowset fetchAll("bug_status = 'NEW'"); ]]> Retrieving Rows from a Rowset The Rowset itself is usually less interesting than the Rows that it contains. This section illustrates how to get the Rows that comprise the Rowset. A legitimate query returns zero rows when no rows in the database match the query conditions. Therefore, a Rowset object might contain zero Row objects. Since Zend_Db_Table_Rowset_Abstract implements the Countable interface, you can use count() to determine the number of Rows in the Rowset. Counting the Rows in a Rowset fetchAll("bug_status = 'FIXED'"); $rowCount = count($rowset); if ($rowCount > 0) { echo "found $rowCount rows"; } else { echo 'no rows matched the query'; } ]]> Reading a Single Row from a Rowset The simplest way to access a Row from a Rowset is to use the current() method. This is particularly appropriate when the Rowset contains exactly one Row. fetchAll("bug_id = 1"); $row = $rowset->current(); ]]> If the Rowset contains zero rows, current() returns PHP's null value. Iterating through a Rowset Objects descending from Zend_Db_Table_Rowset_Abstract implement the SeekableIterator interface, which means you can loop through them using the foreach construct. Each value you retrieve this way is a Zend_Db_Table_Row_Abstract object that corresponds to one record from the table. fetchAll(); foreach ($rowset as $row) { // output 'Zend_Db_Table_Row' or similar echo get_class($row) . "\n"; // read a column in the row $status = $row->bug_status; // modify a column in the current row $row->assigned_to = 'mmouse'; // write the change to the database $row->save(); } ]]> Seeking to a known position into a Rowset SeekableIterator allows you to seek to a position that you would like the iterator to jump to. Simply use the seek() method for that. Pass it an integer representing the number of the Row you would like your Rowset to point to next, don't forget that it starts with index 0. If the index is wrong, ie doesn't exist, an exception will be thrown. You should use count() to check the number of results before seeking to a position. fetchAll(); // takes the iterator to the 9th element (zero is one element) : $rowset->seek(8); // retrive it $row9 = $rowset->current(); // and use it $row9->assigned_to = 'mmouse'; $row9->save(); ]]> getRow() allows you to get a specific row in the Rowset, knowing its position; don't forget however that positions start with index zero. The first parameter for getRow() is an integer for the position asked. The second optional parameter is a boolean; it tells the Rowset iterator if it must seek to that position in the same time, or not (default is false). This method returns a Zend_Db_Table_Row object by default. If the position requested does not exist, an exception will be thrown. Here is an example : fetchAll(); // retrieve the 9th element immediately: $row9->getRow(8); // and use it: $row9->assigned_to = 'mmouse'; $row9->save(); ]]> After you have access to an individual Row object, you can manipulate the Row using methods described in . Retrieving a Rowset as an Array You can access all the data in the Rowset as an array using the toArray() method of the Rowset object. This returns an array containing one entry per Row. Each entry is an associative array having keys that correspond to column names and elements that correspond to the respective column values. Using toArray() fetchAll(); $rowsetArray = $rowset->toArray(); $rowCount = 1; foreach ($rowsetArray as $rowArray) { echo "row #$rowCount:\n"; foreach ($rowArray as $column => $value) { echo "\t$column => $value\n"; } ++$rowCount; echo "\n"; } ]]> The array returned from toArray() is not updateable. That is, you can modify values in the array as you can with any array, but changes to the array data are not propagated to the database. Serializing and Unserializing a Rowset Objects of type Zend_Db_Table_Rowset_Abstract are serializable. In a similar fashion to serializing an individual Row object, you can serialize a Rowset and unserialize it later. Serializing a Rowset Simply use PHP's serialize() function to create a string containing a byte-stream representation of the Rowset object argument. fetchAll(); // Convert object to serialized form $serializedRowset = serialize($rowset); // Now you can write $serializedRowset to a file, etc. ]]> Unserializing a Serialized Rowset Use PHP's unserialize() function to restore a string containing a byte-stream representation of an object. The function returns the original object. Note that the Rowset object returned is in a disconnected state. You can iterate through the Rowset and read the Row objects and their properties, but you cannot change values in the Rows or execute other methods that require a database connection (for example, queries against related tables). current(); echo $row->bug_description; ]]> Why do Rowsets unserialize in a disconnected state? A serialized object is a string that is readable to anyone who possesses it. It could be a security risk to store parameters such as database account and password in plain, unencrypted text in the serialized string. You would not want to store such data to a text file that is not protected, or send it in an email or other medium that is easily read by potential attackers. The reader of the serialized object should not be able to use it to gain access to your database without knowing valid credentials. You can reactivate a disconnected Rowset using the setTable() method. The argument to this method is a valid object of type Zend_Db_Table_Abstract, which you create. Creating a Table object requires a live connection to the database, so by reassociating the Table with the Rowset, the Rowset gains access to the database. Subsequently, you can change values in the Row objects contained in the Rowset and save the changes to the database. Reactivating a Rowset as Live Data setTable($bugs); $row = $rowset->current(); // Now you can make changes to the row and save them $row->bug_status = 'FIXED'; $row->save(); ]]> Reactivating a Rowset with setTable() also reactivates all the Row objects contained in that Rowset. Extending the Rowset class You can use an alternative concrete class for instances of Rowsets by extending Zend_Db_Table_Rowset_Abstract. Specify the custom Rowset class by name either in the $_rowsetClass protected member of a Table class, or in the array argument of the constructor of a Table object. Specifying a custom Rowset class 'MyRowset')); ]]> Typically, the standard Zend_Db_Rowset concrete class is sufficient for most usage. However, you might find it useful to add new logic to a Rowset, specific to a given Table. For example, a new method could calculate an aggregate over all the Rows in the Rowset. Example of Rowset class with a new method updated_at > $max_updated_at) { $latestRow = $row; } } return $latestRow; } } class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bugs'; protected $_rowsetClass = 'MyBugsRowset'; } ]]> Zend_Db_Table Relationships Introduction Tables have relationships to each other in a relational database. An entity in one table can be linked to one or more entities in another table by using referential integrity constraints defined in the database schema. The Zend_Db_Table_Row class has methods for querying related rows in other tables. Defining Relationships Define classes for each of your tables, extending the abstract class Zend_Db_Table_Abstract, as described in . Also see for a description of the example database for which the following example code is designed. Below are the PHP class definitions for these tables: array( 'columns' => 'reported_by', 'refTableClass' => 'Accounts', 'refColumns' => 'account_name' ), 'Engineer' => array( 'columns' => 'assigned_to', 'refTableClass' => 'Accounts', 'refColumns' => 'account_name' ), 'Verifier' => array( 'columns' => array('verified_by'), 'refTableClass' => 'Accounts', 'refColumns' => array('account_name') ) ); } class BugsProducts extends Zend_Db_Table_Abstract { protected $_name = 'bugs_products'; protected $_referenceMap = array( 'Bug' => array( 'columns' => array('bug_id'), 'refTableClass' => 'Bugs', 'refColumns' => array('bug_id') ), 'Product' => array( 'columns' => array('product_id'), 'refTableClass' => 'Products', 'refColumns' => array('product_id') ) ); } ]]> If you use Zend_Db_Table to emulate cascading UPDATE and DELETE operations, declare the $_dependentTables array in the class for the parent table. List the class name for each dependent table. Use the class name, not the physical name of the SQL table. Skip declaration of $_dependentTables if you use referential integrity constraints in the RDBMS server to implement cascading operations. See for more information. Declare the $_referenceMap array in the class for each dependent table. This is an associative array of reference "rules". A reference rule identifies which table is the parent table in the relationship, and also lists which columns in the dependent table reference which columns in the parent table. The rule key is a string used as an index to the $_referenceMap array. This rule key is used to identify each reference relationship. Choose a descriptive name for this rule key. It's best to use a string that can be part of a PHP method name, as you will see later. In the example PHP code above, the rule keys in the Bugs table class are: 'Reporter', 'Engineer', 'Verifier', and 'Product'. The value of each rule entry in the $_referenceMap array is also an associative array. The elements of this rule entry are described below: columns => A string or an array of strings naming the foreign key column name(s) in the dependent table. It's common for this to be a single column, but some tables have multi-column keys. refTableClass => The class name of the parent table. Use the class name, not the physical name of the SQL table. It's common for a dependent table to have only one reference to its parent table, but some tables have multiple references to the same parent table. In the example database, there is one reference from the bugs table to the products table, but three references from the bugs table to the accounts table. Put each reference in a separate entry in the $_referenceMap array. refColumns => A string or an array of strings naming the primary key column name(s) in the parent table. It's common for this to be a single column, but some tables have multi-column keys. If the reference uses a multi-column key, the order of columns in the 'columns' entry must match the order of columns in the 'refColumns' entry. It is optional to specify this element. If you don't specify the refColumns, the column(s) reported as the primary key columns of the parent table are used by default. onDelete => The rule for an action to execute if a row is deleted in the parent table. See for more information. onUpdate => The rule for an action to execute if values in primary key columns are updated in the parent table. See for more information. Fetching a Dependent Rowset If you have a Row object as the result of a query on a parent table, you can fetch rows from dependent tables that reference the current row. Use the method: findDependentRowset($table, [$rule]); ]]> This method returns a Zend_Db_Table_Rowset_Abstract object, containing a set of rows from the dependent table $table that refer to the row identified by the $row object. The first argument $table can be a string that specifies the dependent table by its class name. You can also specify the dependent table by using an object of that table class. Fetching a Dependent Rowset This example shows getting a Row object from the table Accounts, and finding the Bugs reported by that account. find(1234); $user1234 = $accountsRowset->current(); $bugsReportedByUser = $user1234->findDependentRowset('Bugs'); ]]> The second argument $rule is optional. It is a string that names the rule key in the $_referenceMap array of the dependent table class. If you don't specify a rule, the first rule in the array that references the parent table is used. If you need to use a rule other than the first, you need to specify the key. In the example code above, the rule key is not specified, so the rule used by default is the first one that matches the parent table. This is the rule 'Reporter'. Fetching a Dependent Rowset By a Specific Rule This example shows getting a Row object from the table Accounts, and finding the Bugs assigned to be fixed by the user of that account. The rule key string that corresponds to this reference relationship in this example is 'Engineer'. find(1234); $user1234 = $accountsRowset->current(); $bugsAssignedToUser = $user1234->findDependentRowset('Bugs', 'Engineer'); ]]> You can also add criteria, ordering and limits to your relationships using the parent row's select object. Fetching a Dependent Rowset using a Zend_Db_Table_Select This example shows getting a Row object from the table Accounts, and finding the Bugs assigned to be fixed by the user of that account, limited only to 3 rows and ordered by name. find(1234); $user1234 = $accountsRowset->current(); $select = $accountsTable->select()->order('name ASC') ->limit(3); $bugsAssignedToUser = $user1234->findDependentRowset('Bugs', 'Engineer', $select); ]]> Alternatively, you can query rows from a dependent table using a special mechanism called a "magic method". Zend_Db_Table_Row_Abstract invokes the method: findDependentRowset('<TableClass>', '<Rule>') if you invoke a method on the Row object matching either of the following patterns: $row->find<TableClass>() $row->find<TableClass>By<Rule>() In the patterns above, <TableClass> and <Rule> are strings that correspond to the class name of the dependent table, and the dependent table's rule key that references the parent table. Some application frameworks, such as Ruby on Rails, use a mechanism called "inflection" to allow the spelling of identifiers to change depending on usage. For simplicity, Zend_Db_Table_Row does not provide any inflection mechanism. The table identity and the rule key named in the method call must match the spelling of the class and rule key exactly. Fetching Dependent Rowsets using the Magic Method This example shows finding dependent Rowsets equivalent to those in the previous examples. In this case, the application uses the magic method invocation instead of specifying the table and rule as strings. find(1234); $user1234 = $accountsRowset->current(); // Use the default reference rule $bugsReportedBy = $user1234->findBugs(); // Specify the reference rule $bugsAssignedTo = $user1234->findBugsByEngineer(); ]]> Fetching a Parent Row If you have a Row object as the result of a query on a dependent table, you can fetch the row in the parent to which the dependent row refers. Use the method: findParentRow($table, [$rule]); ]]> There always should be exactly one row in the parent table referenced by a dependent row, therefore this method returns a Row object, not a Rowset object. The first argument $table can be a string that specifies the parent table by its class name. You can also specify the parent table by using an object of that table class. Fetching the Parent Row This example shows getting a Row object from the table Bugs (for example one of those bugs with status 'NEW'), and finding the row in the Accounts table for the user who reported the bug. fetchAll(array('bug_status = ?' => 'NEW')); $bug1 = $bugsRowset->current(); $reporter = $bug1->findParentRow('Accounts'); ]]> The second argument $rule is optional. It is a string that names the rule key in the $_referenceMap array of the dependent table class. If you don't specify a rule, the first rule in the array that references the parent table is used. If you need to use a rule other than the first, you need to specify the key. In the example above, the rule key is not specified, so the rule used by default is the first one that matches the parent table. This is the rule 'Reporter'. Fetching a Parent Row By a Specific Rule This example shows getting a Row object from the table Bugs, and finding the account for the engineer assigned to fix that bug. The rule key string that corresponds to this reference relationship in this example is 'Engineer'. fetchAll(array('bug_status = ?', 'NEW')); $bug1 = $bugsRowset->current(); $engineer = $bug1->findParentRow('Accounts', 'Engineer'); ]]> Alternatively, you can query rows from a parent table using a "magic method". Zend_Db_Table_Row_Abstract invokes the method: findParentRow('<TableClass>', '<Rule>') if you invoke a method on the Row object matching either of the following patterns: $row->findParent<TableClass>([Zend_Db_Table_Select $select]) $row->findParent<TableClass>By<Rule>([Zend_Db_Table_Select $select]) In the patterns above, <TableClass> and <Rule> are strings that correspond to the class name of the parent table, and the dependent table's rule key that references the parent table. The table identity and the rule key named in the method call must match the spelling of the class and rule key exactly. Fetching the Parent Row using the Magic Method This example shows finding parent Rows equivalent to those in the previous examples. In this case, the application uses the magic method invocation instead of specifying the table and rule as strings. fetchAll(array('bug_status = ?', 'NEW')); $bug1 = $bugsRowset->current(); // Use the default reference rule $reporter = $bug1->findParentAccounts(); // Specify the reference rule $engineer = $bug1->findParentAccountsByEngineer(); ]]> Fetching a Rowset via a Many-to-many Relationship If you have a Row object as the result of a query on one table in a many-to-many relationship (for purposes of the example, call this the "origin" table), you can fetch corresponding rows in the other table (call this the "destination" table) via an intersection table. Use the method: findManyToManyRowset($table, $intersectionTable, [$rule1, [$rule2, [Zend_Db_Table_Select $select] ] ]); ]]> This method returns a Zend_Db_Table_Rowset_Abstract containing rows from the table $table, satisfying the many-to-many relationship. The current Row object $row from the origin table is used to find rows in the intersection table, and that is joined to the destination table. The first argument $table can be a string that specifies the destination table in the many-to-many relationship by its class name. You can also specify the destination table by using an object of that table class. The second argument $intersectionTable can be a string that specifies the intersection table between the two tables in the the many-to-many relationship by its class name. You can also specify the intersection table by using an object of that table class. Fetching a Rowset with the Many-to-many Method This example shows getting a Row object from from the origin table Bugs, and finding rows from the destination table Products, representing products related to that bug. find(1234); $bug1234 = $bugsRowset->current(); $productsRowset = $bug1234->findManyToManyRowset('Products', 'BugsProducts'); ]]> The third and fourth arguments $rule1 and $rule2 are optional. These are strings that name the rule keys in the $_referenceMap array of the intersection table. The $rule1 key names the rule for the relationship from the intersection table to the origin table. In this example, this is the relationship from BugsProducts to Bugs. The $rule2 key names the rule for the relationship from the intersection table to the destination table. In this example, this is the relationship from Bugs to Products. Similarly to the methods for finding parent and dependent rows, if you don't specify a rule, the method uses the first rule in the $_referenceMap array that matches the tables in the relationship. If you need to use a rule other than the first, you need to specify the key. In the example code above, the rule key is not specified, so the rules used by default are the first ones that match. In this case, $rule1 is 'Reporter' and $rule2 is 'Product'. Fetching a Rowset with the Many-to-many Method By a Specific Rule This example shows geting a Row object from from the origin table Bugs, and finding rows from the destination table Products, representing products related to that bug. find(1234); $bug1234 = $bugsRowset->current(); $productsRowset = $bug1234->findManyToManyRowset('Products', 'BugsProducts', 'Bug'); ]]> Alternatively, you can query rows from the destination table in a many-to-many relationship using a "magic method." Zend_Db_Table_Row_Abstract invokes the method: findManyToManyRowset('<TableClass>', '<IntersectionTableClass>', '<Rule1>', '<Rule2>') if you invoke a method matching any of the following patterns: $row->find<TableClass>Via<IntersectionTableClass> ([Zend_Db_Table_Select $select]) $row->find<TableClass>Via<IntersectionTableClass>By<Rule1> ([Zend_Db_Table_Select $select]) $row->find<TableClass>Via<IntersectionTableClass>By<Rule1>And<Rule2> ([Zend_Db_Table_Select $select]) In the patterns above, <TableClass> and <IntersectionTableClass> are strings that correspond to the class names of the destination table and the intersection table, respectively. <Rule1> and <Rule2> are strings that correspond to the rule keys in the intersection table that reference the origin table and the destination table, respectively. The table identities and the rule keys named in the method call must match the spelling of the class and rule key exactly. Fetching Rowsets using the Magic Many-to-many Method This example shows finding rows in the destination table of a many-to-many relationship representing products related to a given bug. find(1234); $bug1234 = $bugsRowset->current(); // Use the default reference rule $products = $bug1234->findProductsViaBugsProducts(); // Specify the reference rule $products = $bug1234->findProductsViaBugsProductsByBug(); ]]> Cascading Write Operations Declare DRI in the database: Declaring cascading operations in Zend_Db_Table is intended only for RDBMS brands that do not support declarative referential integrity (DRI). For example, if you use MySQL's MyISAM storage engine, or SQLite, these solutions do not support DRI. You may find it helpful to declare the cascading operations with Zend_Db_Table. If your RDBMS implements DRI and the ON DELETE and ON UPDATE clauses, you should declare these clauses in your database schema, instead of using the cascading feature in Zend_Db_Table. Declaring cascading DRI rules in the RDBMS is better for database performance, consistency, and integrity. Most importantly, do not declare cascading operations both in the RDBMS and in your Zend_Db_Table class. You can declare cascading operations to execute against a dependent table when you apply an UPDATE or a DELETE to a row in a parent table. Example of a Cascading Delete This example shows deleting a row in the Products table, which is configured to automatically delete dependent rows in the Bugs table. find(1234); $product1234 = $productsRowset->current(); $product1234->delete(); // Automatically cascades to Bugs table // and deletes dependent rows. ]]> Similarly, if you use UPDATE to change the value of a primary key in a parent table, you may want the value in foreign keys of dependent tables to be updated automatically to match the new value, so that such references are kept up to date. It's usually not necessary to update the value of a primary key that was generated by a sequence or other mechanism. But if you use a natural key that may change value occasionally, it is more likely that you need to apply cascading updates to dependent tables. To declare a cascading relationship in the Zend_Db_Table, edit the rules in the $_referenceMap. Set the associative array keys 'onDelete' and 'onUpdate' to the string 'cascade' (or the constant self::CASCADE). Before a row is deleted from the parent table, or its primary key values updated, any rows in the dependent table that refer to the parent's row are deleted or updated first. Example Declaration of Cascading Operations In the example below, rows in the Bugs table are automatically deleted if the row in the Products table to which they refer is deleted. The 'onDelete' element of the reference map entry is set to self::CASCADE. No cascading update is done in the example below if the primary key value in the parent class is changed. The 'onUpdate' element of the reference map entry is self::RESTRICT. You can get the same result using the value self::NO_ACTION, or by omitting the 'onUpdate' entry. array( 'columns' => array('product_id'), 'refTableClass' => 'Products', 'refColumns' => array('product_id'), 'onDelete' => self::CASCADE, 'onUpdate' => self::RESTRICT ), ... ); } ]]> Notes Regarding Cascading Operations Cascading operations invoked by Zend_Db_Table are not atomic. This means that if your database implements and enforces referential integrity constraints, a cascading UPDATE executed by a Zend_Db_Table class conflicts with the constraint, and results in a referential integrity violation. You can use cascading UPDATE in Zend_Db_Table only if your database does not enforce that referential integrity constraint. Cascading DELETE suffers less from the problem of referential integrity violations. You can delete dependent rows as a non-atomic action before deleting the parent row that they reference. However, for both UPDATE and DELETE, changing the database in a non-atomic way also creates the risk that another database user can see the data in an inconsistent state. For example, if you delete a row and all its dependent rows, there is a small chance that another database client program can query the database after you have deleted the dependent rows, but before you delete the parent row. That client program may see the parent row with no dependent rows, and assume this is the intended state of the data. There is no way for that client to know that its query read the database in the middle of a change. The issue of non-atomic change can be mitigated by using transactions to isolate your change. But some RDBMS brands don't support transactions, or allow clients to read "dirty" changes that have not been committed yet. Cascading operations in Zend_Db_Table are invoked only by Zend_Db_Table. Cascading deletes and updates defined in your Zend_Db_Table classes are applied if you execute the save() or delete() methods on the Row class. However, if you update or delete data using another interface, such as a query tool or another application, the cascading operations are not applied. Even when using update() and delete() methods in the Zend_Db_Adapter class, cascading operations defined in your Zend_Db_Table classes are not executed. No Cascading INSERT. There is no support for a cascading INSERT. You must insert a row to a parent table in one operation, and insert row(s) to a dependent table in a separate operation.
Zend_Debug Mostrar información de variables(Dumping Variables) El método estático Zend_Debug::dump() imprime o devuelve información sobre una expresión. Esta sencilla técnica de depuración es común, porque es fácil de utilizar en caliente y no requiere inicialización, herramientas especiales, o la depuración del entorno. Ejemplo del método dump() El argumento $var especifica la expresión o variable sobre la cual el método Zend_Debug::dump() generará información. El argumento boleano $echo especifica si la salida de Zend_Debug::dump() es o no mostrada. Si es verdadera, la salida es mostrada. A pesar del valor del argumento $echo, el retorno de este método contiene la salida. Puede ser útil comprender que el método Zend_Debug::dump() envuelve la función de PHP var_dump(). Si el flujo de salida es detectado como una presentación de la web, la salida de var_dump() es escapada usando htmlspecialchars() y envuelta con el tag (X)HTML pre. Depurando con Zend_Log Usar Zend_Debug::dump() es lo mejor para la depuración en caliente durante el desarrollo de software. Puede añadir el código para volcar una variable y después quitar el código fácilmente. También considere el componente Zend_Log escribiendo el código de depuración más permanente. Por ejemplo, puede utilizar el nivel de log de DEPURACIÓN y el Stream log writer, para mostrar la cadena de salida de Zend_Debug::dump(). Zend_Dojo Zend_Dom Zend_Exception Usando Excepciones Todas las excepciones lanzadas por las clases de Zend Framework deberian arrojar una excepción que deriva de la clase base Zend_Exception. Ejemplo de catching de una excepción getMessage() . "\n"; // código para recuperar el fallo. } ]]> Consulte la documentación de cada uno de los componente de Zend Framework para obtener información más específica sobre los métodos que lanzan excepciones, las circunstancias de las excepciones, y qué clases derivan de Zend_Exception. Zend_Feed Zend_File Zend_Filter Zend_Form Zend_Form Zend_Form simplifica la creación y manejo de formularios en sus aplicaciones web. Cumple los siguientes objetivos: Validación y filtrado de la información suministrada Ordenado de elementos Elementos y su presentación, incluyendo su escape Agrupación de elementos y formularios Configuración de componentes a nivel de formulario Se aprovecha en gran medida de otros componentes de Zend Framework para lograr sus objetivos, incluyendo Zend_Config, Zend_Validate, Zend_Filter, Zend_Loader_PluginLoader y, opcionalmente, Zend_View. Inicio rápido a Zend_Form Esta guía rápida pretende cubrir los fundamentos para crear, validar y presentar formularios usando Zend_Form Creando un objeto formulario Crear un objeto de formulario es muy simple: solo instancíe Zend_Form Para casos de uso avanzados, es posible desee crear una subclase de Zend_Form, pero para formularios simples, puede programar la creación de un formulario usando un objeto Zend_Form Si desea especificar el action y method del formulario (siempre buenas ideas), puede hacer uso de los accesos setAction() y setMethod(): setAction('/resource/process') ->setMethod('post'); ]]> El código de arriba establece el action del formulario a la URL parcial "/resource/process" y como method HTTP POST. Esto se mostrará en la presentación final. Usted puede establecer atributos HTML adicionales para la etiqueta <form> mediante el uso de los métodos setAttrib() o setAttribs(). Por ejemplo, si desea especificar el id establezca el atributo "id": setAttrib('id', 'login'); ]]> Añadir elementos al formulario Un formulario no es nada sin sus elementos. Zend_Form contiene de manera predeterminada algunos elementos que generan XHTML vía auxiliares Zend_View. Son los siguientes: button checkbox (o varios checkboxes a la vez con multiCheckbox) hidden image password radio reset select (tanto regulares como de multi-selección) submit text textarea Tiene dos opciones para añadir elementos a un formulario; puede instanciar elementos concretos y pasarlos como objetos, o simplemente puede pasar el tipo de elemento y Zend_Form instaciará por usted un objeto del tipo correspondiente. Algunos ejemplos: addElement(new Zend_Form_Element_Text('username')); // Pasando el tipo de elemento del formulario al objeto form: $form->addElement('text', 'username'); ]]> De manera predeterminada, éstos no tienen validadores o filtros. Esto significa que tendrá que configurar sus elementos con un mínimo de validadores, y potencialmente filtros. Puede hacer esto (a) antes de pasar el elemento al formulario, (b) vía opciones de configuración pasadas cuando crea un elemento a través de Zend_Form, o (c) recuperar el elemento del objeto form y configurándolo posteriormente. Veamos primero la creación de validadores para la instancia de un elemento concreto. Puede pasar objetos Zend_Validate_* o el nombre de un validador para utilizar: addValidator(new Zend_Validate_Alnum()); // Pasando el nombre de un validador: $username->addValidator('alnum'); ]]> Cuando se utiliza esta segunda opción, si el constructor del validador acepta argumentos, se pueden pasar en un array como tercer parámetro: addValidator('regex', false, array('/^[a-z]/i')); ]]> (El segundo parámetro se utiliza para indicar si el fallo debería prevenir la ejecución de validadores posteriores o no; por defecto, el valor es false.) Puede también desear especificar un elemento como requerido. Esto puede hacerse utilizando un método de acceso o pasando una opción al crear el elemento. En el primer caso: setRequired(true); ]]> Cuando un elemento es requerido, un validador 'NotEmpty' (NoVacio) es añadido a la parte superior de la cadena de validaciones, asegurando que el elemento tenga algún valor cuando sea requerido. Los filtros son registrados básicamente de la misma manera que los validadores. Para efectos ilustrativos, vamos a agregar un filtro para poner en minúsculas el valor final: addFilter('StringtoLower'); ]]> Entonces, la configuración final de nuestro elemento queda así: addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]/')) ->setRequired(true) ->addFilter('StringToLower'); // o, de manera más compacta: $username->addValidators(array('alnum', array('regex', false, '/^[a-z]/i') )) ->setRequired(true) ->addFilters(array('StringToLower')); ]]> Tan simple como esto, realizarlo para cada uno de los elementos del formulario puede resultar un poco tedioso. Intentemos la opción (b) arriba mencionada. Cuando creamos un nuevo elemento utilizando Zend_Form::addElement() como fábrica, opcionalmente podemos pasar las opciones de configuración. Éstas pueden incluir validadores y los filtros que se van a utilizar. Por lo tanto, para hacer todo lo anterior implícitamente, intente lo siguiente: addElement('text', 'username', array( 'validators' => array( 'alnum', array('regex', false, '/^[a-z]/i') ), 'required' => true, 'filters' => array('StringToLower'), )); ]]> Si encuentra que está asignando elementos con las mismas opciones en varios lugares, podría considerar crear su propia subclase de Zend_Form_Element y utilizar ésta; a largo plazo le permitirá escribir menos. Generar un formulario Generar un formulario es simple. La mayoría de los elementos utilizan un auxiliar de Zend_View para generarse a sí mismos, por lo tanto necesitan un objeto vista con el fin de generarse. Además, tiene dos opciones: usar el método render() del formulario, o simplemente mostrarlo con echo. render($view); // Suponiendo un objeto vista ha sido previamente establecido vía setView(): echo $form; ]]> De manera predeterminada, Zend_Form y Zend_Form_Element intentarán utilizar el objeto vista inicializado en el ViewRenderer, lo que significa que no tendrá que establecer la vista manualmente cuando use el MVC de Zend Framework. Generar un formulario en un script vista es tan simple como: form ?> ]]> Detrás del telón, Zend_Form utiliza "decoradores" (decorators) para generar la salida. Estos decoradores pueden reemplazar, añadir o anteponer contenido, y tienen plena introspección al elemento que les es pasado. Como resultado, puede combinar múltiples decoradores para lograr efectos personalizados. Predeterminadamente, Zend_Form_Element actualmente combina cuatro decoradores para obtener su salida; la configuración sería como sigue: addDecorators(array( 'ViewHelper', 'Errors', array('HtmlTag', array('tag' => 'dd')), array('Label', array('tag' => 'dt')), )); ]]> (Donde <HELPERNAME> es el nombre de un view helper que utilizar, y varía según el elemento) Lo anterior crea una salida como la siguiente: (Aunque no con el mismo formato.) Puede cambiar los decoradores usados para un elemento si desea tener diferente salida; véase la sección sobre decoradores para mayor información. El propio formulario simplemente itera sobre los elementos y los cubre en un <form> HTML. El action y method que proporcionó cuando definió el formulario se pasan a la etiqueta <form>, como cualquier atributo que establezca vía setAttribs() y familia. Elementos son desplegados en el orden en el que fueron registrados, o, si el elemento contienen un atributo de orden, ese orden será utilizado. Usted puede fijar el orden de un elemento usando: setOrder(10); ]]> O, cuando crea un elemento, pasándolo como una opción: addElement('text', 'username', array('order' => 10)); ]]> Comprobar si un formulario es válido Después que un formulario es enviado, necesitará comprobar y ver si pasa las validaciones. Cada elemento es valuado contra los datos provistos; si una clave no está presente y el campo fue marcado como requerido, la validación se ejecuta contra un valor nulo. ¿De dónde provienen los datos?. Puede usar $_POST o $_GET, o cualquier otra fuente de datos que tenga a mano (solicitud de un servicio web, por ejemplo): isValid($_POST)) { // ¡Correcto! } else { // ¡Fallo! } ]]> Con solicitudes AJAX, a veces puede ignorar la validación de un solo elemento, o grupo de elementos. isValidPartial() validará parcialmente el formulario. A diferencia de isValid(), que como sea, si alguna clave no esta presente, no ejecutará las validaciones para ese elemento en particular. isValidPartial($_POST)) { // de los elementos presentes, todos pasaron las validaciones } else { // uno u más elementos probados no pasaron las validaciones } ]]> Un método adicional, processAjax(), puede también ser usado para validar formularios parciales. A diferencia de isValidPartial(), regresa una cadena en formato JSON conteniendo mensajes de error en caso de fallo. Asumiendo que sus validaciones han pasado, ahora puede obtener los valores filtrados: getValues(); ]]> Si necesita los valores sin filtrar en algún punto, utilice: getUnfilteredValues(); ]]> Obteniendo el estado de error Entonces, ¿su formulario no es válido? En la mayoría de los casos, simplemente puede generar el formulario nuevamente y los errores se mostrarán cuando se usen los decoradores predeterminados: isValid($_POST)) { echo $form; // o asigne al objeto vista y genere una vista... $this->view->form = $form; return $this->render('form'); } ]]> Si quiere inspeccionar los errores, tiene dos métodos. getErrors() regresa una matriz asociativa de nombres / códigos de elementos (donde códigos es una matriz de códigos de error). getMessages() regresa una matriz asociativa de nombres / mensajes de elementos (donde mensajes es una matriz asociativa de pares código de error / mensaje de error). Si un elemento no tiene ningún error, no será incluido en la matriz. Poniendo todo junto Construyamos un simple formulario de login. Necesitaremos elementos que representen: usuario contraseña Botón de ingreso Para nuestros propósitos, vamos a suponer que un usuario válido cumplirá con tener solo caracteres alfanuméricos, comenzar con una letra, tener una longitud mínima de 6 caracteres y una longitud máxima de 20 caracteres; se normalizarán en minúsculas. Las contraseñas deben tener un mínimo de 6 caracteres. Cuando se procese vamos simplemente a mostrar el valor, por lo que puede permanecer inválido. Usaremos el poder de la opciones de configuración de Zend_Form para crear el formulario: setAction('/user/login') ->setMethod('post'); // Crea un y configura el elemento username $username = $form->createElement('text', 'username'); $username->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->addValidator('stringLength', false, array(6, 20)) ->setRequired(true) ->addFilter('StringToLower'); // Crea y configura el elemento password: $password = $form->createElement('password', 'password'); $password->addValidator('StringLength', false, array(6)) ->setRequired(true); // Añade los elementos al formulario: $form->addElement($username) ->addElement($password) // uso de addElement() como fábrica para crear el botón 'Login': ->addElement('submit', 'login', array('label' => 'Login')); ]]> A continuación, vamos a crear un controlador para manejar esto: view->form = $this->getForm(); $this->render('form'); } public function loginAction() { if (!$this->getRequest()->isPost()) { return $this->_forward('index'); } $form = $this->getForm(); if (!$form->isValid($_POST)) { // Falla la validación; Se vuelve a mostrar el formulario $this->view->form = $form; return $this->render('form'); } $values = $form->getValues(); // ahora intenta y autentica... } } ]]> Y un script para la vista que muestra el formulario: Please login: form ?> ]]> Como notará en el código del controlador, hay más trabajo por hacer: mientras la información enviada sea válida, necesitará todavía realizar la autenticación usando Zend_Auth, por ejemplo. Usando un objeto Zend_Config Todas las clases Zend_Form son configurables mediante Zend_Config; puede incluso pasar un objeto al constructor o pasarlo a través de setConfig(). Veamos cómo podemos crear el formulario anterior usando un archivo INI. Primero, vamos a seguir las recomendaciones, y colocaremos nuestras configuraciones dentro de secciones reflejando su objetivo y y enfocándonos en la sección 'development'. A continuación, pondremos en una sección de configuración para el controlador dado ('user'), y una clave para el formulario ('login'): Entonces puede pasarlo al constructor del formulario: user->login); ]]> y el formulario entero será definido. Conclusión Esperamos que después de este pequeño tutorial sea capaz de descubrir el poder y flexibilidad de Zend_Form. Continúe leyendo para profundizar más en el tema. Creando elementos de formulario usando Zend_Form_Element Un formulario esta compuesto de elementos, que normalmente corresponden al elemento HTML input. Zend_Form_Element encapsula elementos de formulario individualmente, con las siguientes áreas de responsabilidad: validación (¿los datos enviados son válidos?) captura de códigos y mensajes de error filtrado (¿cómo es escapado y normalizado el elemento para su validación y/o salida? generación (¿cómo es mostrado el elemento?) metadatos y atributos (¿qué información amplía la definición del elemento?) La clase base, Zend_Form_Element, funciona por defecto para varios casos, pero es mejor extender la clase para elementos con fines especiales de uso común. Adicionalmente, Zend Framework contiene un número de elementos XHTML estándar; puede leer de ellos en el capítulo Elementos Estándares Cargadores de Plugin Zend_Form_Element hace uso de Zend_Loader_PluginLoader para permitir a los desarrolladores especificar ubicaciones de validadores, filtros y decoradores alternos. Cada uno tiene su propio cargador de plugin asociado a él y métodos de acceso generales usados para su recuperación y modificación. Los siguientes tipos de cargadores son usados con los varios métodos del cargador de plugin: 'validate', 'filter', y 'decorator'. Los nombres son sensibles a mayúsculas y minúsculas. Los métodos usados para interactuar con los cargadores de plugin son los siguientes: setPluginLoader($loader, $type): $loader es el propio objeto cargador, mientras $type es uno de los tipos arriba mencionados. Esto establece el cargador de plugin para el tipo dado en el objeto cargador recién especificado. getPluginLoader($type): obtiene el cargador de plugin asociado con $type. addPrefixPath($prefix, $path, $type = null): agrega una asociación prefijo/ruta para el cargador especificado por $type. Si $type es null, se intentará agregar la ruta a todos los cargadores, añadiendo el prefijo a cada "_Validate", "_Filter" y "_Decorator"; y agregandole "Validate/", "Filter/" y "Decorator/" a la ruta. Si tiene todas sus clases extras para elementos de formulario dentro de una jerarquía común, este método es conveniente para establecer el prefijo para todas ellas. addPrefixPaths(array $spec): le permite añadir varias rutas de una sola vez a uno o más cargadores de plugin. Se espera cada elemento de la matriz sea un array con claves 'path', 'prefix', y 'type'. Validadores, filtros y decoradores personalizados son una manera simple de compartir funcionalidad entre formularios y encapsular funcionalidad personalizada. Etiqueta personalizada Un uso común de los plugins es proveer reemplazos para las clases estándares. Por ejemplo, si desea proveer una implementación diferente del decorador 'Label' -- por ejemplo, para añadir siempre dos puntos -- puede crear su propio decorador 'Label' con su propio prefijo de clase, y entonces añadirlo a su prefijo de ruta. Comencemos con un decorador de etiqueta personalizado. Le daremos el prefijo "My_Decorator", y la clase estará en el archivo "My/Decorator/Label.php". getElement())) { return $content; } if (!method_exists($element, 'getLabel')) { return $content; } $label = $element->getLabel() . ':'; if (null === ($view = $element->getView())) { return $this->renderLabel($content, $label); } $label = $view->formLabel($element->getName(), $label); return $this->renderLabel($content, $label); } public function renderLabel($content, $label) { $placement = $this->getPlacement(); $separator = $this->getSeparator(); switch ($placement) { case 'APPEND': return $content . $separator . $label; case 'PREPEND': default: return $label . $separator . $content; } } } ]]> Ahora diremos al elemento que use esta ruta cuando busque por decoradores: addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator'); ]]> Alternativamente, podemos hacerlo en el formulario para asegurar que todos los decoradores usen esta ruta: addElementPrefixPath('My_Decorator', 'My/Decorator/', 'decorator'); ]]> Con esta ruta añadida, cuando agregue un decorador, la ruta 'My/Decorator' será consultada primero en búsqueda de la existencia del decorador en este lugar. Como resultado, 'My_Decorator_Label' ahora será utilizado cuando el decorador 'Label' sea requerido. Filters A menudo es útil y/o necesario realizar alguna normalización en la entrada antes de la validación – por ejemplo, puede querer eliminar todo el HTML, pero realizar las validaciones sobre lo restante para asegurarse que el envío es válido. O puede eliminar los espacios en blanco al inicio o fin de la entrada para asegurarse de que un validador StringLenth (longitud de la cadena) no regrese un positivo falso. Estas operaciones pueden realizarse usando Zend_Filter, y Zend_Form_Element que soportan cadenas de filtros, permitiéndole especificar múltiples filtros secuenciales a utilizar. El filtrado sucede tanto en la validación como cuando recupera el valor del elemento vía getValue(): getValue(); ]]> Los filtros pueden ser agregados a la pila de dos maneras: pasándolo en una instancia de filtro específica proveyendo un nombre de filtro – el correspondiente nombre corto o completo de la clase Veamos algunos ejemplos: addFilter(new Zend_Filter_Alnum()); // El correspondiente nombre completo de la clase: $element->addFilter('Zend_Filter_Alnum'); // Nombre corto del filtro: $element->addFilter('Alnum'); $element->addFilter('alnum'); ]]> Los nombres cortos son típicamente el nombre del filtro sin el prefijo. En el caso predeterminado, esto se refiere a sin el prefijo 'Zend_Filter_'. Además, la primera letra no necesita estar en mayúscula. Usando clases de filtros personalizados Si tiene su propio conjunto de clases de filtro, puede informarle de ellas a Zend_Form_Element usando addPrefixPath(). Por ejemplo, si tiene filtros con el prefijo 'My_Filter', puede indicárselo a Zend_Form_Element de la siguiente manera: addPrefixPath('My_Filter', 'My/Filter/', 'filter'); ]]> (Recuerde que el tercer argumento indica el cargador de plugin sobre el cual ha de ejecutarse la acción.) Si en algún momento necesita un valor no filtrado, use el método getUnfilteredValue(): getUnfilteredValue(); ]]> Para mayor información sobre filtros, vea la documentación de Zend_Filter. Métodos asociados con filtros incluyen: addFilter($nameOfFilter, array $options = null) addFilters(array $filters) setFilters(array $filters) (sobreescribe todos los filtros) getFilter($name) (recupera un objeto filtro por su nombre) getFilters() (recupera todos los filtros) removeFilter($name) (elimina un filtro por su nombre) clearFilters() (elimina todos los filtros) Validadores Si sigue el mantra de seguridad "filtrar la entrada, escapar la salida" querrá validar ("filtrar la entrada") los datos de los formularios. En Zend_Form cada elemento contiene su propia cadena de validadores, consistente en validadores Zend_Validate_*. Los validadores pueden ser agregados de dos maneras: pasándolo en una instancia de validador específica proveyendo un nombre de validador – el correspondiente nombre corto o completo de clase Veamos algunos ejemplos: addValidator(new Zend_Validate_Alnum()); // El correspondiente nombre completo de la clase: $element->addValidator('Zend_Validate_Alnum'); // Nombre corto del validador: $element->addValidator('Alnum'); $element->addValidator('alnum'); ]]> Los nombres cortos son típicamente el nombre del validador sin el prefijo. En el caso predeterminado, esto se refiere a sin el prefijo 'Zend_Validate_'. Además, la primera letra no necesita estar en mayúscula. Usando clases de validación personalizadas Si tiene su propio conjunto de clases de validación, puede informarle de ellas a Zend_Form_Element usando addPrefixPath(). Por ejemplo, si tiene validadores con el prefijo 'My_Validator', puede indicárselo a Zend_Form_Element de la siguiente manera: addPrefixPath('My_Validator', 'My/Validator/', 'validate'); ]]> (Recuerde que el tercer argumento indica el cargador de plugin sobre el cual ha de ejecutarse la acción.) Si el fallo de un validador debe evitar validaciones posteriores, pase el boleano true como segundo parámetro: addValidator('alnum', true); ]]> Si está usando la cadena nombre para añadir el validador, y la clase del validador acepta argumentos para su constructor, puede pasarlos a el tercer parámetro de addValidator() como un array: addValidator('StringLength', false, array(6, 20)); ]]> Los argumentos pasados de esta manera deben estar en el orden en el cual son definidos en el constructor. El ejemplo de arriba instanciará la clase Zend_Validate_StringLenth con los parámetros $min y $max: Estipulando mensajes de error de validación personalizados Algunos desarrolladores querrán estipular mensajes de error personalizados para un validador. El argumento $options de Zend_Form_Element::addValidator() le permite hacerlo proporcionando la clave 'messages' y estableciendolos en un array de pares clave/valor para especificar las plantillas de mensaje. Necesitará conocer los códigos de error de los diferentes tipos de error de un validador en particular. Una opción mejor es usar Zend_Translate_Adapter con su formulario. Los códigos de error son automáticamente pasados al adaptador por el decorador Errors por defecto; puede especificar su propias cadenas de mensaje de error mediante la creación de traducciones para los varios códigos de error de sus validadores. Puede también establecer varios validadores a la vez, usando addValidators(). Su uso básico es pasar una matriz de arrays, donde cada array contenga de 1 a 3 valores, correspondientes al constructor de addValidator(): addValidators(array( array('NotEmpty', true), array('alnum'), array('stringLength', false, array(6, 20)), )); ]]> Si quiere ser más detallado o explícito, puede utilizar las claves 'validator', 'breakChainOnFailure', y 'options' en el array: addValidators(array( array( 'validator' => 'NotEmpty', 'breakChainOnFailure' => true), array('validator' => 'alnum'), array( 'validator' => 'stringLength', 'options' => array(6, 20)), )); ]]> Este uso es bueno para ilustrar cómo puede configurar validadores en un archivo de configuración: Note que cada elemento tiene una clave, la necesite o no; esta es una limitación del uso de archivos de configuración -- pero también ayuda a hacer más explicito el para qué son usados los argumentos. Sólo recuerde que cualesquiera opciones del validador deben ser especificadas en orden. Para validar un elemento, pase el valor a isValid(): isValid($value)) { // válido } else { // no válido } ]]> Validación operando en valores filtrados Zend_Form_Element::isValid() siempre filtra los valores antes de la validación a través de la cadena de filtros. Vea la sección de filtros para más información. Contexto de validación Zend_Form_Element::isValid() soporta un argumento adicional, $context. Zend_Form::isValid() pasa todo el conjunto de datos procesados a $context cuando valida un formulario, y Zend_Form_Element::isValid(), a su vez, lo pasa a cada validador. Esto significa que puede escribir validadores que son conscientes de los datos pasados a otros elementos del formulario. Como ejemplo, considere un formulario de registro estándar que tiene campos para la contraseña y la confirmación de la contraseña; una validación sería que los dos campos coincidan. Este validador puede tener un aspecto como el siguiente: 'Password confirmation does not match' ); public function isValid($value, $context = null) { $value = (string) $value; $this->_setValue($value); if (is_array($context)) { if (isset($context['password_confirm']) && ($value == $context['password_confirm'])) { return true; } } elseif (is_string($context) && ($value == $context)) { return true; } $this->_error(self::NOT_MATCH); return false; } } ]]> Los validadores son procesados en orden. Cada validador es procesado, a menos que un validador creado con un valor true para breakChainOnFailure falle su validación. Asegúrese de especificar sus validadores en un orden razonable. Después de una validación fallida, puede recuperar los códigos y mensajes de error de la cadena del validador: getErrors(); $messages = $element->getMessages(); ]]> (Nota: los mensajes de error retornados son un array asociativo de pares código / mensaje de error.) En adición a los validadores, puede especificar que un elemento es necesario, usando setRequired(true). Por defecto, esta bandera es false, lo que significa que pasará su cadena de validadores si ningún valor es pasado a isValid(). Puede modificar este comportamiento en un número de maneras: Por defecto, cuando un elemento es requerido, una bandera, 'allowEmpty', también es true. Esto quiere decir que si un valor empty es evaluado pasándolo a isValid(), los validadores serán saltados. Puede intercalar esta bandera usando el método de acceso setAllowEmpty($flag); cuando la bandera es false, si un valor es pasado, los validadores seguirán ejecutándose. Por defecto, si un elemento es requerido, pero no contiene un validador 'NotEmpty', isValid() añadirá uno en la cima de la pila, con la bandera breakChainOnFailure establecido. Esto hace que la bandera requerida tenga un significado semántico: si ningún valor es pasado, inmediatamente invalidamos el envío y se le notifica al usuario, e impedimos que otros validadores se ejecuten en lo que ya sabemos son datos inválidos. Si no quiere este comportamiento, puede desactivarlo pasando un valor false a setAutoInsertNotEmptyValidator($flag); esto prevendrá a isValid() de colocar un validador 'NotEmpty' en la cadena de validaciones. Para mayor información sobre validadores, vea la documentación de Zend_Validate. Usando Zend_Form_Elements como validador de propósito general Zend_Form_Element implementa Zend_Validate_Interface, significando un elemento puede también usarse como un validador en otro, cadenas de validación no relacionadas al formulario. Métodos asociados con validación incluyen: setRequired($flag) y isRequired() permiten establecer y recuperar el estado de la bandera 'required'. Cuando se le asigna un booleano true, esta bandera requiere que el elemento esté presente en la información procesada por Zend_Form. setAllowEmpty($flag) y getAllowEmpty() permiten modificar el comportamiento de elementos opcionales (p.e., elementos donde la bandera required es false). Cuando la bandera 'allow empty' es true, valores vacíos no pasarán la cadena de validadores. setAutoInsertNotEmptyValidator($flag) permite especificar si realmente un validador 'NotEmpty' será añadido el inicio de la cadena de validaciones cuando un elemento es requerido. Por defecto, esta bandera es true. addValidator($nameOrValidator, $breakChainOnFailure = false, array $options = null) addValidators(array $validators) setValidators(array $validators) (sobreescribe todos los validadores) getValidator($name) (recupera un objeto validador por nombre) getValidators() (recupera todos los validadores) removeValidator($name) (elimina un validador por nombre) clearValidators() (elimina todos los validadores) Errores de mensaje personalizados Alguna veces, querrá especificar uno o más mensajes de error para usarlos en lugar de los mensajes de error generados por los validadores adjuntos a los elementos. Adicionalmente, algunas veces usted mismo querrá marcar al elemento como inválido. A partir de 1.6.0, esta funcionalidad es posible vía los siguientes métodos. addErrorMessage($message): añade un mensaje de error para mostrarlos en forma de errores de validación. Puede llamarlo más de una vez, y los nuevos mensajes nuevos son añadidos a la pila. addErrorMessages(array $messages): añade múltiples mensajes de error para mostrarlos en forma de errores de validación. setErrorMessages(array $messages): añade múltiples mensajes de error para mostrarlos en forma de errores de validación, sobreescribiendo todos los mensajes de error previamente establecidos. getErrorMessages(): recupera la lista de mensajes de error personalizados que fueron definidos. clearErrorMessages(): remueve todos los mensajes de error personalizados que hayan sido definidos. markAsError(): marca al elemento como que falló la validación. hasErrors(): determina si el elemento ha fallado la validación o ha sido marcado como inválido. addError($message): añade un mensaje a la pila de mensaje de error personalizados y marca al elemento como inválido. addErrors(array $messages): añade varios mensajes a la pila de mensajes de error personalizados y marca al elemento como inválido. setErrors(array $messages): sobreescribe el mensaje de error personalizado en la pila con los mensajes previstos y marca al elemento como inválido. Todos los errores establecidos de este modo pueden ser traducidos. Adicionalmente, puede insertar el marcador "%value%" para representar el valor del elemento; este valor actual del elemento será sustituido cuando el mensaje de error sea recuperado. Decoradores Una dolencia particular para muchos desarrolladores web es la creación del XHTML para formularios por ellos mismos. Para cada elemento, el desarrollador necesita crear la marcación para el elemento mismo, comúnmente una etiqueta (label), y, si son amables con sus usuarios, la marcación para mostrar mensajes de errores de validación. Cuanto más elementos en una página, menos trivial se convierte esta tarea. Zend_Form_Element intenta resolver este problema mediante el uso de "decoradores". Los decoradores son clases simples que tienen métodos de acceso al elemento y métodos para generar el contenido. Para obtener mayor información sobre cómo trabajan los decoradores, consulte por favor la sección sobre Zend_Form_Decorator. Los decoradores usados por defecto por Zend_Form_Element son: ViewHelper: especifica un view helper que usar para general el elemento. El atributo 'helper' del elemento puede usarse para especificar qué auxiliar vista usar. Por defecto, Zend_Form_Element especifica el auxiliar vista 'formText', pero cada subclase especifica diferentes auxiliares. Errors: añade mensajes de error al elemento usando Zend_View_Helper_FormErrors. Si no está presente, no se añade nada. Description: añade la descripción del elemento. Si no está presente, no se añade nada. Por defecto, la descripción es generada dentro de una etiqueta <p> con un class 'description'. HtmlTag: envuelve el elemento y los errores en una etiqueta HTML <dd>. Label: añade al comienzo una etiqueta al elemento usando Zend_View_Helper_FormLabel, y envolviéndola en una etiqueta <dt>. Si ninguna etiqueta es provista, solo la etiqueta de la definición es generada. Decoradores por defecto no necesitan ser cargados Por defecto, los decoradores por defecto son cargados durante la inicialización del objeto. Puede deshabilitar esto pasando la opción 'disableLoadDefaultDecorators' al constructor: true) ); ]]> Esta opción puede ser combinada junto con cualquier otra opción que pase, ya sea como un array de opciones o en un objeto Zend_Config. Ya que el orden en el cual los decoradores son registrados importa -- el primer decorador registrado es ejecutado primero -- necesitará estar seguro de registrar sus decoradores en el orden apropiado, o asegurarse de que estableció las opciones de colocación en el modo apropiado. Por dar un ejemplo, aquí esta el código que registran los decoradores por defecto: addDecorators(array( array('ViewHelper'), array('Errors'), array('Description', array('tag' => 'p', 'class' => 'description')), array('HtmlTag', array('tag' => 'dd')), array('Label', array('tag' => 'dt')), )); ]]> El contenido inicial es creado por el decorador 'ViewHelper', que crea el propio elemento. En seguida, el decorador 'Errors' consulta los mensajes de error del elemento, y, si hay alguno presente, los pasa al auxiliar vista 'FormErrors' para mostrarlos. Si una descripción está presente, el decorador 'Description' añadirá un párrafo con class 'description' conteniendo el texto descriptivo para el contenido agregado. El siguiente decorador, 'HtmlTag', envuelve al elemento, los errores, y la descripción en una etiqueta HTML <dd>. Finalmente, el último decorador, 'label', recupera la etiqueta del elemento y la pasa al auxiliar vista 'FormLabel', envolviéndolo en una etiqueta <dt>; por default el valor es añadido al inicio del contenido. El resultado de la salida básicamente se ve así:
  • "123" is not an alphanumeric value

This is some descriptive text regarding the element.

]]>
Para más información sobre decoradores, lea la sección de Zend_Form_Decorator. Usando múltiples decoradores al mismo tiempo Internamente, Zend_Form_Element utiliza una clase decorador como mecanismo de búsqueda para la recuperación de decoradores. Como resultado, no puede registrar múltiples decoradores del mismo tipo; decoradores subsecuentes simplemente sobreescribirán aquellos que ya existían. Para evitar esto, puede usar alias. En lugar de pasar un decorador o nombre de decorador como primer argumento a addDecorator(), pase una matriz con un solo elemento, con el alias apuntando al nombre o objeto decorador: addDecorator(array('FooBar' => 'HtmlTag'), array('tag' => 'div')); // Y recuperandolo posteriormente: $decorator = $element->getDecorator('FooBar'); ]]> En los métodos addDecorators() y setDecorators(), necesitará pasar la opción 'decorator' en la matriz representando el decorador: addDecorators( array('HtmlTag', array('tag' => 'div')), array( 'decorator' => array('FooBar' => 'HtmlTag'), 'options' => array('tag' => 'dd') ), ); // Y recuperándolos posteriormente: $htmlTag = $element->getDecorator('HtmlTag'); $fooBar = $element->getDecorator('FooBar'); ]]> Métodos asociados con decoradores incluyen: addDecorator($nameOrDecorator, array $options = null) addDecorators(array $decorators) setDecorators(array $decorators) (sobreescribe todos los decoradores) getDecorator($name) (recupera un objeto decorador por su nombre) getDecorators() (recupera todos los decoradores) removeDecorator($name) (elimina un decorador por su nombre) clearDecorators() (elimina todos los decoradores) Zend_Form_Element también utiliza la sobrecarga para permitir generar decoradores específicos. __call() interceptará métodos que comiencen con el texto 'render' y utilizará el resto del nombre del método para buscar un decorador; si se encuentra, entonces será generado sólo ese decorador. Cualquier argumento pasado al llamado del método será usado como contenido para pasar al método render() del decorador. Como ejemplo: renderViewHelper(); // Genera solo el decorador HtmlTag, pasándole contenido: echo $element->renderHtmlTag("This is the html tag content"); ]]> Si el decorador no existe, una excepción es lanzada.
Metadatos y atributos Zend_Form_Element manipula una variedad de atributos y medatados del elemento. Atributos básicos incluyen: name: el nombre del elemento. Emplea los métodos de acceso setName() y getName(). label: la etiqueta del elemento. Emplea los métodos de acceso setLabel() y getLabel(). order: el índice en el cual los elementos deben ir mostrándose en el formulario. Emplea los métodos de acceso setOrder() y getOrder(). value: El valor del elemento actual. Emplea los métodos de acceso setValue() y getValue(). description: una descripción del elemento; a menudo utilizada para proveer un tooltip o ayuda contextual con javascript describiendo el propósito del elemento. Emplea los métodos de acceso setDescription() y getDescription(). required: bandera que indica si un elemento es requerido o no cuando se efectúa la validación del formulario. Emplea los métodos de acceso setRequired() y getRequired(). Esta bandera es false por defecto. allowEmpty: bandera indicando si un elemento no-requerido (opcional) debe intentar validar o no valores vacíos. Cuando es true, y la bandera required es false, valores vacíos no pasarán la cadena de validación, y se supone verdadero. Emplea los métodos de acceso setAllowEmpty() y getAllowEmpty(). Esta bandera es true por defecto. autoInsertNotEmptyValidator: bandera indicando insertar o no un validador 'NotEmpty' cuando un elemento es requerido. Por defecto, esta bandera es true. Establezca la bandera con setAutoInsertNotEmptyValidator($flag) y determine el valor con autoInsertNotEmptyValidator(). Los elementos del formulario pueden requerir metainformación adicional. Para elementos XHTML del formuladio, por ejemplo, puede querer especificar atributos como el class o id. Para facilitar esto hay un conjunto de métodos de acceso: setAttrib($name, $value): añade un atributo setAttribs(array $attribs): como addAttribs(), pero sobreescribiendo getAttrib($name): recupera el valor de solo un atributo getAttribs(): recupera todos los atributos como pares clave/valor La mayoría de las veces, como sea, puede simplemente acceder a ellos como propiedades de objeto, ya que Zend_Form_Element utiliza la sobrecarga para facilitar el acceso a ellos: setAttrib('class', 'text'): $element->class = 'text; ]]> Por defecto, todos los atributos son pasados al auxiliar vista usado por el elemento durante la generación, y generados como atributos de la etiqueta del elemento. Elementos Estándar Zend_Form contiene un buen número de elementos estándar; por favor lea el capítulo Elementos Estándar para todos los detalles. Métodos de Zend_Form_Element Zend_Form_Element tiene muchos, muchos métodos. Lo que sigue es un sumario de sus funciones, agrupados por tipo: Configuración: setOptions(array $options) setConfig(Zend_Config $config) I18n: setTranslator(Zend_Translate_Adapter $translator = null) getTranslator() setDisableTranslator($flag) translatorIsDisabled() Propiedades: setName($name) getName() setValue($value) getValue() getUnfilteredValue() setLabel($label) getLabel() setDescription($description) getDescription() setOrder($order) getOrder() setRequired($flag) getRequired() setAllowEmpty($flag) getAllowEmpty() setAutoInsertNotEmptyValidator($flag) autoInsertNotEmptyValidator() setIgnore($flag) getIgnore() getType() setAttrib($name, $value) setAttribs(array $attribs) getAttrib($name) getAttribs() Cargadores y rutas de plugin: setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type) getPluginLoader($type) addPrefixPath($prefix, $path, $type = null) addPrefixPaths(array $spec) Validación: addValidator($validator, $breakChainOnFailure = false, $options = array()) addValidators(array $validators) setValidators(array $validators) getValidator($name) getValidators() removeValidator($name) clearValidators() isValid($value, $context = null) getErrors() getMessages() Filtros: addFilter($filter, $options = array()) addFilters(array $filters) setFilters(array $filters) getFilter($name) getFilters() removeFilter($name) clearFilters() Generación: setView(Zend_View_Interface $view = null) getView() addDecorator($decorator, $options = null) addDecorators(array $decorators) setDecorators(array $decorators) getDecorator($name) getDecorators() removeDecorator($name) clearDecorators() render(Zend_View_Interface $view = null) Configuración El constructor de Zend_Form_Element acepta tanto una matriz de opciones como un objeto Zend_Config conteniendo opciones, y esto puede configurarse usando setOptions() o setConfig(). Hablando de manera general, las claves son nombradas de la siguiente manera: Si 'set' + clave se refiere a un método de Zend_Form_Element, entonces el valor provisto será pasado a el método. De otra manera, el valor será usado para establecer un atributo. Excepciones a la regla incluyen las siguientes: prefixPath será pasado a addPrefixPaths() Los siguientes setters no pueden establecerse de esta manera: setAttrib (aunque setAttribs funcionará setConfig setOptions setPluginLoader setTranslator setView Como ejemplo, aquí esta un archivo de configuración pasado para cada tipo de dato configurable: Elementos personalizados Usted puede crear sus propios elementos personalizados simplemente extendiendo la clase Zend_Form_Element. Las razones comunes para hacer esto incluyen: Elementos que comparten validadores y/o filtros comunes Elementos que tienen decoradores con funcionalidad personalizada Hay dos métodos típicamente usados para extender un elemento: init(), el cual puede usarse para añadir una lógica de inicialización personalizada a su elemento, y loadDefaultDecorators(), el cual puede usarse para establecer una lista de decoradores usados por su elemento de manera predeterminada. Como un ejemplo, digamos que todos los elementos de tipo texto en un formulario que está creando, necesitan ser filtrados con StringTrim, validados con una expresión regular, y que quiere usar un decorador personalizado que ha creado para mostrarlos, 'My_Decorator_TextItem'; adicionalmente, tiene un número de atributos estándars, incluyendo 'size', 'maxLength', y 'class' que quisiera especificar. Puede definir un elemento tal como sigue: addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator') ->addFilters('StringTrim') ->addValidator('Regex', false, array('/^[a-z0-9]{6,}$/i')) ->addDecorator('TextItem') ->setAttrib('size', 30) ->setAttrib('maxLength', 45) ->setAttrib('class', 'text'); } } ]]> Entonces puede informar a su objeto formulario acerca del prefijo de ruta para elementos de ese tipo, y comenzar creando elementos: addPrefixPath('My_Element', 'My/Element/', 'element') ->addElement('foo', 'text'); ]]> El elemento 'foo' será ahora del tipo My_Element_Text, y mostrará el comportamiento que ha especificado. Otro método que puede querer sobreescribir cuando extienda Zend_Form_Element es el método loadDefaultDecorators(). Este método carga condicionalmente un grupo de decoradores predefinidos para su elemento; puede querer sustituir su propio decorador en su clase extendida: addDecorator('ViewHelper') ->addDecorator('DisplayError') ->addDecorator('Label') ->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'element')); } } ]]> Hay muchas maneras de personalizar elementos; asegúrese de leer la documentación de la API de Zend_Form_Element para conocer todos los métodos disponibles.
Creando formularios usando Zend_Form La clase Zend_Form es usada para agregar elementos de formulario, desplegar grupos y subformularios. Éstos pueden ejecutar las siguientes acciones en estos elementos: Validación, incluyendo la recuperación de código y mensajes de error Agregación de valor, incluyendo el llenado de elementos y recuperación de valores tanto filtrados como no filtrados para todos los elementos Iteración sobre todos los elementos, en el orden en el cual han sido introducidos o basados en el orden recuperado de cada elemento Generando el formulario entero, ya sea por un simple decorador que ejecuta un muestreo personalizado o por iteración sobre cada elemento del formulario Mientras los formularios creados con Zend_Form pueden ser complejos, probablemente su mejor uso es para formularios simples; es mejor utilizarlo para desarrollar aplicaciones rápidas y de prototipado. En lo más básico, simplemente instancie el objeto formulario: Opcionalmente puede pasarlo en la configuración, la cual será usada para establecer el estado del objeto, potencialmente así como también crear nuevos elementos: Zend_Form es iterable, e iterará sobre elementos, grupos que mostrar y subformularios, usando el orden en el cual han sido registrados y en cualquier índice de orden que cada uno pueda tener. Esto es útil en casos donde desee generar los elementos manualmente en el orden apropiado. La magia de Zend_Form radica en la habilidad para servir como fábrica para elementos y grupos, así como también la habilidad de generarse a sí mismo a través de decoradores. Cargadores de Plugin Zend_Form hace uso de Zend_Loader_PluginLoader para permitir a los desarroladores especificar la ubicación de elementos y decoradores alternativos. Cada uno tiene su propio plugin loader asociado, y métodos de acceso genéricos son usados para recuperar y modificar cada uno. Los siguientes tipos de cargadores son usados con los variados métodos del plugin loader: 'element' y 'decorator'. Los nombres de los tipos no distinguen mayúsculas de minúsculas. Los métodos usados para interactuar con plugin loaders son los siguientes: setPluginLoader($loader, $type): $loader es el propio objeto plugin loader, mientras $type es uno de los tipos especificados arriba. Esto establece el plugin loader para el tipo dado al objeto loader recién especificado. getPluginLoader($type): recupera el plugin loader asociado con $type. addPrefixPath($prefix, $path, $type = null): agrega una asociación prefijo/ruta al loader especificado por $type. Si $type es nulo, intentará añadir una ruta a todos los loaders, añadiendo el prefijo "_Element" y "_Decorator"; y añadiendo la ruta con "Element/" y "Decorator/". Si tiene todas sus clases form element extras bajo una jerarquía común, éste es un método coveniente para establecer el prefijo de base para ellas. addPrefixPaths(array $spec): le permite añadir varias rutas en uno o mas plugin loaders. Se espera que cada elemento del array sea un array con las claves 'path', 'prefix' y 'type'. Adicionalmente, puede especificar prefijos de rutas para todos los elementos y mostrar grupos creados a través de una instancia de Zend_Form usando los siguientes métodos: addElementPrefixPath($prefix, $path, $type = null): Igual que addPrefixPath(), debe especificar un prefijo y ruta de clase. $type, cuando se especifica, tiene que ser uno de los tipos plugin loader especificados por Zend_Form_Element; vea la sección elemento plugins para más información de valores válidos para $type. Si $type no es especificado, el método asumirá que es un prefijo general para todos los tipos. addDisplayGroupPrefixPath($prefix, $path): Igual que addPrefixPath(), debe especificar un prefijo y ruta de clase; sin embargo, dado que los grupos de visualización (display groups) sólo soportan decoradores como plugins, $type no es necesario. Elementos y decoradores personalizados son una manera fácil de compartir funcionalidad entre formularios y encapsular funcionalidad personalizada. Vea el ejemplo de Etiqueta Personalizada en la documentación de elementos para un ejemplo de cómo elementos personalizados pueden ser usados como reemplazos para clases estándar. Elementos Zend_Form proporciona varios métodos de acceso para añadir y eliminar elementos de el formulario. Éstos pueden tomar instancias de objetos de elemento o servir como fábricas para instanciar el objeto elemento a sí mismo. El método más básico para añadir un elemento es addElement(). Este método puede tomar también un objeto de tipo Zend_Form_Element (o de una clase extendiendo Zend_Form_Element), o argumentos para construir un nuevo elemento -- incluyendo el elemento tipo, nombre y algunas opciones de configuración. Como algunos ejemplos: addElement($element); // Usando una fábrica // // Crea un elemento de tipo Zend_Form_Element_Text con el // nombre de 'foo': $form->addElement('text', 'foo'); // Pasa una opción etiqueta al elemento: $form->addElement('text', 'foo', array('label' => 'Foo:')); ]]> addElement() Implementa una Interfaz Fluida addElement() implementa una interfaz fluida; es decir, retorna el objeto Zend_Form y no un elemento. Esto se ha hecho para permitirle encadenar multiples métodos addElement() u otros métodos formulario que implementen una interfaz fluida (todos los establecedores en Zend_Form implementan el patrón). Si desea retornar el elemento, use createElement(), el cual es esbozado abajo. Tenga en cuenta de cualquier manera que createElement() no adjunta el elemento al formulario. Internamente, addElement() en realidad emplea createElement() para crear el elemento antes de adjuntarlo al formulario. Una vez que el elemento ha sido añadido al formulario, puede recuperarlo por el nombre. Puede también finalizar usando el método getElement() o usando sobrecarga para acceder al elemento como una propiedad de objeto: getElement('foo'); // Como propiedad del objeto: $foo = $form->foo; ]]> Ocasionalmente, se quiere crear un elemento sin adjuntarlo al formulario (para instanciar, si se desea hacer uso de las rutas de plugin introducidas con el formulario, pero después se desea adjuntar el objeto al subformulario). El método createElement() permite hacer eso: createElement('text', 'username'); ]]> Llenar y recuperar valores Después de validar el formulario, originalmente se necesitará recuperar los valores para poder ejecutar otras operaciones, tal como actualizar una base de datos o notificar un servicio web. Se pueden recuperar todos los valores para todos los elementos usando getValues(); getValue($name) le permite recuperar un solo valor del elemento por su nombre: getValues(); // Obtener sólo los valores del elemento 'foo': $value = $form->getValue('foo'); ]]> A veces se quiere llenar el formulario con valores especificos antes de generarlos. Éstos pueden ser llevados a cabo ya sea con los métodos setDefaults() o populate(): setDefaults($data); $form->populate($data); ]]> Por otro lado, si se quisera limpiar el formulario antes de llenarlo o validarlo; se puede realizar usando el método reset(): reset(); ]]> Operaciones Globales Ocasionalemnte se necesitarán ciertas operaciones que afecten a todos los elementos. Escenarios comunes incluyen la necesidad de determinar rutas de acceso al prefijo complemento para todos los elementos, determinando decoradores para todos los elementos y determinando filtros para todos los elementos. Como ejemplos: Determinando rutas de acceso de prefijos para todos los elementos Se puede determinar rutas de acceso para prefijos para todos los elementos por tipo, o usando un prefijo global. Como ejemplos: addElementPrefixPath('My_Foo', 'My/Foo/'); // Sólo rutas de acceso de filtros: $form->addElementPrefixPath('My_Foo_Filter', 'My/Foo/Filter', 'filter'); // Sólo rutas de acceso de validadores: $form->addElementPrefixPath('My_Foo_Validate', 'My/Foo/Validate', 'validate'); // Sólo rutas de acceso de decoradores: $form->addElementPrefixPath('My_Foo_Decorator', 'My/Foo/Decorator', 'decorator'); ]]> Determinando Decoradores para todos los elementos Se pueden determinar decoradores para todos los elementos. setElementDecorators() acepta una matriz de decoradores, solo como setDecorators(), y reescribirá cualquier decorador previamente determinado en cada elemento. En este ejemplo, determinamos los decoradores para simplificar una ViewHelper y una Label: setElementDecorators(array( 'ViewHelper', 'Label' )); ]]> Determinando decoradores para algunos elementos Pueden determinarse también decoradores para un subconjunto de elementos, ya sea por inclusión o exclusión. El segundo argumento setElementDecorators() puede ser un array de nombres de elemento; por defecto, especificar un array de ese tipo determinará los decoradores especificados en esos elementos solamente. Puede tambien pasar un tercer elemento, una bandera indicando si esta lista de elementos es para propósitos de inclusión o exclusión; si es falso, decorará todos los elementos excepto los pasados en la lista, Como uso estándar del método, cualquier decorador pasado reescribirá cualquier decorador previamente determinado en cada elemento. En el siguiente fragmento, indicamos que queremos los decoradores ViewHelper y Label para los elementos 'foo' y 'bar': setElementDecorators( array( 'ViewHelper', 'Label' ), array( 'foo', 'bar' ) ); ]]> Por otro lado, con este fragmento, indicaremos que queremos usar solamente los decoradores ViewHelper y Label para cada elemento excepto los elementos 'foo' y 'bar': setElementDecorators( array( 'ViewHelper', 'Label' ), array( 'foo', 'bar' ), false ); ]]> Algunos Decoradores son Inapropiados para algunos Elementos Mientras setElementDecorators() puede parecer una buena solución, existen algunos casos donde puede terminar con resultados inesperados, Por ejemplo, los muchos elementos botones (Submit, Button, Reset), actualmente usan la etiqueta como el valor del botón y sólo usan los decoradores ViewHelper y DtDdWrapper -- previniendo una etiqueta adicional, errores, y sugerencias de ser generadas; el ejemplo de arriba podría duplicar algún contenido (la etiqueta). Se puede usar el array de inclusión/exclusión para superar este problema como se ha notado en el ejemplo anterior. Entonces, use este método sabiamente y dése cuenta de que puede necesitar excluir o cambiar manualmente algunos elementos decoradores para prevenir una salida no deseada. Determinando Filtros para todos los Elementos En muchos casos, puede quererse aplicar el mismo filtro a todos los elementos; un caso común es trim() a todos los valores: setElementFilters(array('StringTrim')); ]]> Métodos para Interactuar con los Elementos Los siguientes métodos pueden ser usados para interactuar con los elementos: createElement($element, $name = null, $options = null) addElement($element, $name = null, $options = null) addElements(array $elements) setElements(array $elements) getElement($name) getElements() removeElement($name) clearElements() setDefaults(array $defaults) setDefault($name, $value) getValue($name) getValues() getUnfilteredValue($name) getUnfilteredValues() setElementFilters(array $filters) setElementDecorators(array $decorators) addElementPrefixPath($prefix, $path, $type = null) addElementPrefixPaths(array $spec) Grupos de visualización (display groups) Los grupos de visualización (display groups) son una manera de crear grupos virtuales de elementos para propósitos de visualización. Todos los elementos quedan accesibles por nombre en el formulario, pero cuando interactúan o se ejecutan sobre el formulario, cualquiera de los elementos en un grupos de visualización son generados juntos. El caso más común de uso es agrupando los elementos en fieldsets. (TODO) La clase base para los grupos de visualización es Zend_Form_DisplayGroup. Mientras puede ser instanciado directamente, es mejor usar el método addDisplayGroup() de la claseZend_Form. Este método toma un array de elementos como primer argumento y el nombre para el grupo de visualización como segundo argumento. Opcionalmente, se puede pasar en una array de opciones o en un objeto Zend_Config como tercer argumento. Asumiendo que los elementos 'username' y 'password' has sido determinados en el formulario, el siguiente código podría agrupar estos elementos en un grupo de visualización 'login': addDisplayGroup(array('username', 'password'), 'login'); ]]> Puede acceder a los grupos de visualización usando el método getDisplayGroup(), o mediante la sobrecarga usando el nombre del grupo de visualización: getDisplayGroup('login'); // Usando sobrecarga: $login = $form->login; ]]> Decoradores por defecto que no necesitan ser cargados Por defecto, los grupos de visualización son cargados durante la inicialización del objeto. Se puede deshabilitar pasando la opción 'disableLoadDefaultDecorators' cuando se crea un grupo de visualización: addDisplayGroup( array('foo', 'bar'), 'foobar', array('disableLoadDefaultDecorators' => true) ); ]]> Esta opción puede ser una mezcla con otras opciones pasadas, ambas como opciones de array o en el objeto Zend_Config Operaciones Globales Al igual que los elementos, existen algunas operaciones que pueden afectar a todos los grupos de visualización; éstas incluyen determinar decoradores y fijar la ruta de acceso donde buscar los decoradores. Fijando el Prefijo de Ruta del Decorador para todos los Grupos de Visualización Por defecto, los grupos de visualización heredan cualquier ruta de decorador que use el formulario; sin embargo, si deberían buscar en una ruta alternativa, puede usar el método addDisplayGroupPrefixPath() method. addDisplayGroupPrefixPath('My_Foo_Decorator', 'My/Foo/Decorator'); ]]> Fijando Decoradores para Todos los Grupos de Visualización Pueden determinarse decoradores para todos los grupos de visualización, setDisplayGroupDecorators() admite un array de decoradores, al igual que setDecorators(), y sobreescribirá cualquier conjunto de decoradores previo en cada grupo de visualización. En este ejemplo, fijamos los decoradores a un fieldset (el decorador FormElements es necesario para asegurar que los elementos son iterador): setDisplayGroupDecorators(array( 'FormElements', 'Fieldset' )); ]]> Usando Clases de Grupos de Visualización Personalizadas Por defecto, Zend_Form utiliza la clase Zend_Form_DisplayGroup para grupos de visualización. Puede ocurrir que necesite extender esta clase con el fin de obtener una funcionalid personalizada. addDisplayGroup() no permite pasar una instancia determinada, pero permite especificar la clase que usar como una de sus opciones, usando la clave 'displayGroupClass': addDisplayGroup( array('username', 'password'), 'user', array('displayGroupClass' => 'My_DisplayGroup') ); ]]> Si la clase no ha sido todavía cargada, Zend_Form intentará cargarla a través de Zend_Loader. También puede especificar una clase de grupo de visualización por defecto para usar con el formulario, de forma que todos los grupos de visualización creados con el objeto formulario usen esa clase: setDefaultDisplayGroupClass('My_DisplayGroup'); ]]> Esta funcionalidad puede especificarse en configuraciones como 'defaultDisplayGroupClass', y será cargada con antelación para asegurar que todos los grupos de visualización usen esa clase. Métodos para Interactuar con Grupos de Visualización Los siguientes métodos pueden ser usados para interactuar con el grupo de visualización: addDisplayGroup(array $elements, $name, $options = null) addDisplayGroups(array $groups) setDisplayGroups(array $groups) getDisplayGroup($name) getDisplayGroups() removeDisplayGroup($name) clearDisplayGroups() setDisplayGroupDecorators(array $decorators) addDisplayGroupPrefixPath($prefix, $path) setDefaultDisplayGroupClass($class) getDefaultDisplayGroupClass($class) Métodos Zend_Form_DisplayGroup Zend_Form_DisplayGroup tiene los siguientes métodos, agrupados por tipo: Configuración: setOptions(array $options) setConfig(Zend_Config $config) Metadatos: setAttrib($key, $value) addAttribs(array $attribs) setAttribs(array $attribs) getAttrib($key) getAttribs() removeAttrib($key) clearAttribs() setName($name) getName() setDescription($value) getDescription() setLegend($legend) getLegend() setOrder($order) getOrder() Elementos: createElement($type, $name, array $options = array()) addElement($typeOrElement, $name, array $options = array()) addElements(array $elements) setElements(array $elements) getElement($name) getElements() removeElement($name) clearElements() Cargadores Complemento: setPluginLoader(Zend_Loader_PluginLoader $loader) getPluginLoader() addPrefixPath($prefix, $path) addPrefixPaths(array $spec) Decoratores: addDecorator($decorator, $options = null) addDecorators(array $decorators) setDecorators(array $decorators) getDecorator($name) getDecorators() removeDecorator($name) clearDecorators() Generadores: setView(Zend_View_Interface $view = null) getView() render(Zend_View_Interface $view = null) I18n: setTranslator(Zend_Translate_Adapter $translator = null) getTranslator() setDisableTranslator($flag) translatorIsDisabled() Subformularios Los Sub formularios sirven para diferentes propósitos: Crear grupos de elementos lógicos. Dado que los sub formularios son simplemente formularios, se pueden validar subformularios como entidades individuales. Crear formularios multi-páginas. Dado que los sub formularios son simplemente formularios, se puede deplegar un sub formulario por separado por página, incrementando formularios multi-páginas donde cada formulario tiene su propia validación lógica. Solo una vez que todos los sub formularios se validen, el formulario se consideraría completo. Agrupaciones de visualización. Como grupos de visualización, los sub formularios, cuando son generados como parte de un formulario más grande, pueden ser usados para agrupar elementos. Sea consciente, de todas maneras, que el objeto formulario principal no tendrá conocimiento de los elementos en un sub formulario. Un sub formulario puede ser un objeto Zend_Form o mas originalmente, un objeto Zend_Form_SubForm. éste último contiene decoradores apropiados para la inclusión en un formulario extenso (i.e., no se generan adicionales formulario etiquetas HTML, pero si grupos de elementos). Para adjuntar un sub formulario, simplemente añádalo al formulario y déle un nombre: addSubForm($subForm, 'subform'); ]]> Se puede recuperar un sub formulario usando ya sea getSubForm($name) o sobrecarga usando el nombre del sub formulario: getSubForm('subform'); // Usando sobrecarga: $subForm = $form->subform; ]]> Los Subformularios son incluidos en la interacción del formulario, sin embargo los elementos que lo contienen no lo son Operaciones Globales Como los elementos y los grupos de visualización, existen algunas operaciones que pueden afectar a todos los sub formularios. A diferencia de los grupos de visualización y los elementos, sin embargo, los sub formularios heredan más funcionalidad del objeto formulario principal, y la única operación real que puede realizarse globalmente es determinar decoradores para sub formularios. Para este propósito, existe el método setSubFormDecorators(). En el siguiente ejemplo, determinaremos el decorador para todos los subformularios que sera un simple campo (el decorador FormElements es necesario para asegurar que los elementos son iterados): setSubFormDecorators(array( 'FormElements', 'Fieldset' )); ]]> Métodos para interactuar con Sub Formularios Los siguientes métodos pueden ser usados para interactuar con sub formularios: addSubForm(Zend_Form $form, $name, $order = null) addSubForms(array $subForms) setSubForms(array $subForms) getSubForm($name) getSubForms() removeSubForm($name) clearSubForms() setSubFormDecorators(array $decorators) Metadatos y Atributos Mientras la utilidad de un formulario primariamente deriva de los elementos que contiene, también pueden contener otros metadatos, como un nombre (usado a menudo como ID único en el marcado HTML ); la accion y el método del formulario; el número de elementos, grupos y sub formularios que lo contienen; y arbitrariamente metadatos (usualmente usados para determinar atributos HTML para la etiqueta del propio formulario). Se puede determinar y recuperar el nombre del formulario usando el accesor nombre: setName('registration'); // Recuperar el nombre: $name = $form->getName(); ]]> Para determinar la acción (url en el cual se envia el formulario) y método (método por el cual debería enviar, ej. 'POST' or 'GET'), use los accesores acción y método: setAction('/user/login') ->setMethod('post'); ]]> Se puede también especificar el tipo de codificación del fomulario usando el enctype accessors. Zend_Form define dos constantes, Zend_Form::ENCTYPE_URLENCODED y Zend_Form::ENCTYPE_MULTIPART, correspondiente a los valores 'application/x-www-form-urlencoded' y 'multipart/form-data', respectivamente; sin embargo, puede configurarlo con cualquier tipo de codificación. setAction('/user/login') ->setMethod('post') ->setEnctype(Zend_Form::ENCTYPE_MULTIPART); ]]> El método, acción y enctype son solo usados internamente para generar, y no para algún tipo de validación. Zend_Form implementa la interfaz Countable permitiéndole pasarlo como un argumento para contar: Determinar metadatos arbitrariamente se realiza a través de los accesores 'atribs'. Dado que la sobrecarga en Zend_Form es usada para acceder elementos, grupos de visualización y subformularios, este es el único método para acceder a los metadatos. setAttrib('class', 'zend-form') ->addAttribs(array( 'id' => 'registration', 'onSubmit' => 'validate(this)', )); // Recuperando atributos: $class = $form->getAttrib('class'); $attribs = $form->getAttribs(); // Removiendo atributos: $form->removeAttrib('onSubmit'); // Limpiando todos los atributos: $form->clearAttribs(); ]]> Decoradores Crear el marcado para un formulario es a menudo una tarea que consume mucho tiempo, particularmente si se planea reusar el mismo marcado para mostrar acciones tales como validación de errores, enviar valores, etc. La respuesta de Zend_Form a este problema es los decoradores. Los decoradores para objetos Zend_Form pueden ser usados para generar un formulario. El decorador FormElements iterará a través de todos los elementos en un formulario -- elementos, grupos de visualización y subformularios -- y los generará, devolviendo el resultado. Adicionalmente, los decoradores pueden ser usados para envolver el contenido o anteponerlo o postponerlo. Los decoradores por defecto de Zend_Form son FormElements, HtmlTag (envuelve una lista de definición) y Form; el código equivalente para crearlos es como sigue: setDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'dl')), 'Form' )); ]]> Que crea la salida como sigue:
...
]]>
Algunos de los atributos se determinan en el objeto formulario que será usado como atributos HTML de la etiqueta <form>. Decoradores por defecto que no necesitan ser cargados Por defecto, el decorador por defecto son cargados durante la inicialización del objeto. Puede deshabilitarlo pasando la opción 'disableLoadDefaultDecorators' al constructor: true)); ]]> Esta opción puede ser combinada con alguna otra opción que usted pueda pasar, tanto como opciones de array o en un objeto Zend_Config Usando multiples decoradores del mismo tipo Internamente, Zend_Form usa una clase decorador como un mecanismo buscador cuando se recuperan decoradores. Como resultado, no se pueden registrar multiples decoradores del mismo tipo; subsecuentemente los decoradores simplemente sobrescribirán esos decoradores que existían antes. Para conseguir esto, se pueden usar alias. En vez de pasar un decorador o un nombre de decorador como primer argumento a addDecorator(), pase un array con un solo elemento, con el alias apuntando al objeto decorador o nombre: addDecorator(array('FooBar' => 'HtmlTag'), array('tag' => 'div')); // y recuperarlo después: $form = $element->getDecorator('FooBar'); ]]> En los métodos addDecorators() y setDecorators(), se necesitará pasar la opción 'decorator' en el array representando el decorador: addDecorators( array('HtmlTag', array('tag' => 'div')), array( 'decorator' => array('FooBar' => 'HtmlTag'), 'options' => array('tag' => 'dd') ), ); // y recuperándolo después: $htmlTag = $form->getDecorator('HtmlTag'); $fooBar = $form->getDecorator('FooBar'); ]]> Puede crear su propio decorador para generar el formulario. Un caso de uso común es si sabe el HTML exacto que desea usar; su decorador puede crear el mismo HTML y simplemente retornarlo, potencialmente usando los decoradores de individuales elementos o grupos de visualización. Los siguientes métodos puden ser usados para interactuar con decoradores: addDecorator($decorator, $options = null) addDecorators(array $decorators) setDecorators(array $decorators) getDecorator($name) getDecorators() removeDecorator($name) clearDecorators() Zend_Form también usa sobrecarga que permite generar decoradores específicos. __call() interceptará métodos que lleve con el texto 'render' y use el resto del nombre del método para buscar un decorador; si se encuentran, serán generados por un solo decorador. Cualquier argumento pasado a la llamada del método será usado como contenido que pasar al método render() del decorador. Como ejemplo: renderFormElements(); // Generar solo el campo decorador, pasando el contenido: echo $form->renderFieldset("

This is fieldset content

"); ]]>
Si el decorador no existe, una excepción se creará.
Validación Un caso de uso primario para formularios es validar datos enviados. Zend_Form le permite validar un formulario entero de una vez, o una parte de él, asi como también automatizar las respuestas de validación para XmlHttpRequests (AJAX). Si los datos enviados no son válidos, contiene métodos para recuperar los distintos códigos errores y los mensajes de elementos y subformularios de validaciones fallidas. Para validar un formulario entero, use el método isValid(): isValid($_POST)) { // validación fallida } ]]> isValid() validará cada elemento requerido, y algún elemento no requerido contenido en la data sometida. Algunas veces se necesitará validar sólo un subset del dato; para esto use isValidPartial($data): isValidPartial($data)) { // validación fallida } ]]> isValidPartial() sólo intenta validar aquellos elementos en la información para los cuales existen similares elementos; si un elemento es no representado en la información, es pasado por alto. Cuando se validan elementos o grupos de elementos para un requeirimiento AJAX, típicamente se validará un subset del formulario, y quiere la respuesta en JSON. processAjax() precisamente realiza eso: processAjax($data); ]]> Entonces, puede simplemente enviar la respuesta JSON al cliente. Si el formulario es válido, ésta será una respuesta booleana. Si no, será un objeto javascript conteniendo pares de clave/mensaje, donde cada 'message' es un array de validación de mensajes de error. Para los formularios que fallan la validación, se pueden recuperar ambos códigos de error y mensajes de error, usando getErrors() y getMessages(), respectivamente: getErrors(); $messages = $form->getMessage(); ]]> Dado que los mensajes devueltos por getMessages() son un array de pares de errores código/mensaje, getErrors() no es necesario. Puede recuperar códigos y mensajes de error para elementos individuales simplemente pasando el nombre del elemento a cada uno: getErrors('username'); $messages = $form->getMessages('username'); ]]> Nota: Cuando validamos elementos, Zend_Form envía un segundo argumento a cada método isValid() del elemento: el array de los datos que se están validando. Esto puede ser usado por validadores individuales para permitirles utilizar otros valores enviados al determinar la validez de los datos. Un ejemplo sería un formulario de registro que requiere tanto una contraseña como una confirmación de la contraseña; el elemento contraseña puede usar la confirmación de la contraseña como parte de su validación. Mensajes de error personalizados A veces, puede querer especificar uno o más mensajes de error en vez de los mensajes de error generados por los validadores adjuntos a los elementos. Adicionalmente, a veces puede querer marcar el formulario inválido usted mismo. Como 1.6.0, esta funcionalidad es posible siguiendo los métodos. At times, you may want to specify one or more specific error messages to use instead of the error messages generated by the validators attached to your elements. Additionally, at times you may want to mark the form invalid yourself. As of 1.6.0, this functionality is possible via the following methods. addErrorMessage($message): añade un mensaje de error para desplegar en el formulario los errores de validación. Se debe llamar más de una vez, y los nuevos mensajes son adicionados a la pila. addErrorMessages(array $messages): añade múltiples mensajes de error para desplegar en el formulario los errores de validación setErrorMessages(array $messages): añade multiples mensajes de error para desplegar en el formulario los errores de validación, sobrescribiendo todos los mensajes de error previamente determinados. getErrorMessages(): recupera la lista de mensajes de error personalizados que han sido definidos. clearErrorMessages(): remueve todos los mensajes de error personalizados que han sido definidos. markAsError(): marca el formulario como que la validación ha fallado. addError($message): añade un mensaje a la pila de mensajes de error personalizados y señala al formulario como inválido. addErrors(array $messages): añade muchos mensajes a la pila de mensajes de error personalizados y señala al formulario como inválido. setErrors(array $messages): sobrescribe la pila de mensajes de error personalizada con los mensajes proporcionados y señala el formulario como inválido. Todos los errores determinados de esta manera pueden ser traducidos. Métodos La siguiente lista es la lista completa de métodos disponibles para Zend_Form, agrupados por tipo: Configuración y opciones: setOptions(array $options) setConfig(Zend_Config $config) Cargadores de plugins y rutas: setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type = null) getPluginLoader($type = null) addPrefixPath($prefix, $path, $type = null) addPrefixPaths(array $spec) addElementPrefixPath($prefix, $path, $type = null) addElementPrefixPaths(array $spec) addDisplayGroupPrefixPath($prefix, $path) Metadato: setAttrib($key, $value) addAttribs(array $attribs) setAttribs(array $attribs) getAttrib($key) getAttribs() removeAttrib($key) clearAttribs() setAction($action) getAction() setMethod($method) getMethod() setName($name) getName() Elementos: addElement($element, $name = null, $options = null) addElements(array $elements) setElements(array $elements) getElement($name) getElements() removeElement($name) clearElements() setDefaults(array $defaults) setDefault($name, $value) getValue($name) getValues() getUnfilteredValue($name) getUnfilteredValues() setElementFilters(array $filters) setElementDecorators(array $decorators) Subformularios: addSubForm(Zend_Form $form, $name, $order = null) addSubForms(array $subForms) setSubForms(array $subForms) getSubForm($name) getSubForms() removeSubForm($name) clearSubForms() setSubFormDecorators(array $decorators) Grupos de Visualización addDisplayGroup(array $elements, $name, $options = null) addDisplayGroups(array $groups) setDisplayGroups(array $groups) getDisplayGroup($name) getDisplayGroups() removeDisplayGroup($name) clearDisplayGroups() setDisplayGroupDecorators(array $decorators) Validación populate(array $values) isValid(array $data) isValidPartial(array $data) processAjax(array $data) persistData() getErrors($name = null) getMessages($name = null) Generadores: setView(Zend_View_Interface $view = null) getView() addDecorator($decorator, $options = null) addDecorators(array $decorators) setDecorators(array $decorators) getDecorator($name) getDecorators() removeDecorator($name) clearDecorators() render(Zend_View_Interface $view = null) I18n: setTranslator(Zend_Translate_Adapter $translator = null) getTranslator() setDisableTranslator($flag) translatorIsDisabled() Configuración Zend_Form es totalmente configurable mediante setOptions() y setConfig() (o pasando opciones o un objeto Zend_Config al constructor). Usando estos métodos, se pueden especificar elementos formulario, grupos de visualización, decoradores, y metadatos. Como regla general, si 'set' + la clave de opción (option key) hacen referencia a métodos Zend_Form, entonces el valor proporcionado será pasado al método. Si el accessor no existe, se asume que la clave referencia a un atributo, y será pasado a setAttrib(). Excepciones a las reglas incluyen lo siguiente: prefixPaths será pasado a addPrefixPaths() elementPrefixPaths será pasado a addElementPrefixPaths() displayGroupPrefixPaths será pasado a addDisplayGroupPrefixPaths() los siguientes establecedores no pueden ser determinado de ésta manera: setAttrib (aunque setAttribs *funcionará*) setConfig setDefault setOptions setPluginLoader setSubForms setTranslator setView Como un ejemplo, aquí esta un archivo de configuración que pasa la configuración por cada tipo de datos configurables: El código de arriba fácilmente puede ser abstraído a un XML o un archivo de configuración basado en arrays PHP. Formularios personalizados Una alternativa a usar los formularios basados en configuraciones es realizar una subclase de Zend_Form. Esto tiene muchos beneficios: Se puede centrar la prueba de su formulario facilmente para asegurar las validaciones y generar la ejecución esperada. Control preciso sobre los individuales elementos. Reutilización del objeto formulario, y mejor portabilidad (no se necesita seguir los archivos de configuración). Implementar funcionalidad personalizada. El caso mas típico de uso sería el método init() para determinar elementos de formulario específicos y de configuración: class = 'formtext'; $username->setLabel('Username:') ->setDecorators(array( array('ViewHelper', array('helper' => 'formText')), array('Label', array('class' => 'label')) )); $password = new Zend_Form_Element_Password('password'); $password->class = 'formtext'; $password->setLabel('Username:') ->setDecorators(array( array('ViewHelper', array('helper' => 'formPassword')), array('Label', array('class' => 'label')) )); $submit = new Zend_Form_Element_Submit('login'); $submit->class = 'formsubmit'; $submit->setValue('Login') ->setDecorators(array( array('ViewHelper', array('helper' => 'formSubmit')) )); $this->addElements(array( $username, $password, $submit )); $this->setDecorators(array( 'FormElements', 'Fieldset', 'Form' )); } } ]]> Este formulario puede ser instanciado simplemente así: y toda la funcionalidad está instalada y lista; no se necesitan archivos de configuración. (Note que este ejemplo esta simplificado, no contiene validadores o filtros para los elementos.) Otra razón común para la extension es definir un conjunto de decoradores por defecto. Puede hacerlo sobreescribiendo el método loadDefaultDecorators(): setDecorators(array( 'FormElements', 'Fieldset', 'Form' )); } } ]]>
Creando un personalizado marcado de formulario usando Zend_Form_Decorator Representar un objeto form es completamente opcional -- no está obligado a usar los métodos Zend_Form render() en absoluto. Sin embargo, si lo hace, los decoradores se usan para representar distintos objetos form. Un número arbitrario de decoradores pueden estar junto a cada elemento (elements, display groups, sub forms o el objeto form por si mismo); Sin embargo, solo un decorador de un tipo dado puede estar al lado de cada elemento. Los decoradores son llamados en el orden en que han sido introducidos. Dependiendo del decorador, puede reemplazar el contenido que se ha pasado, postponerlo o anteponerlo. El estado del objeto es determinado a través de las opciones de configuración pasadas al constructor o el método decorador setOptions(). Cuando se crean decoradores mediante funciones addDecorator() o métodos relacionados, las opciones pueden ser pasadas como argumentos al método. Esto puese ser usado para una ubicación especifica, un separador se usa entre el contenido pasado y el nuevo contenido generado y cualquier opción que el decorador soporte. Antes de que el render() de cada decorador sea llamado, el item actual es determinado en el decorador usando setElement(), dando al decorador conocimiento del item representado. Esto permite crear decoradores que sólo representan porciones especificas del item -- tal como etiquetas, el valor, mensajes de error, etc. Encadenando muchos decoradores que representan especificos segmentos, puede construir marcados complejos representando al item entero. Operación Para configurar un decorador, pase un array de opciones o un objeto Zend_Config a este constructor, a un array setOptions(), o a un objeto Zend_Config setConfig(). Opciones estándar incluyen: placement: La ubicación puede ser cualquiera de los dos 'append' o 'prepend' (caso insensitivo) e indica cualquier contenido pasado a render() será postpuesto o antepuesto respectivamente. En el caso de que el decorador reemplace el contenido, esta configuración es ignorada. La configuración por defecto es adjuntada. separator: El separator es usado entre el contenido pasado a render() y el nuevo contenido generado por el decorador, o entre items generados por el decorador (ejemplo FormElements usa el separador entre cada item generado). En el caso que un decorador reemplace el contenido, esta configuración puede ser ignorada. El valor por defecto es PHP_EOL. La interface del decorador especifica los métodos para interactuar con las opciones. Esto incluye: setOption($key, $value): determina una sola opción. getOption($key): recuperar un solo valor de opción. getOptions(): recuperar todas las opciones. removeOption($key): eliminar una sola opción. clearOptions(): eliminar todas las opciones. Decoradores son diseñados para interactuar con varios tipos de clases Zend_Form: Zend_Form, Zend_Form_Element, Zend_Form_DisplayGroup, y todas las clases derivan de ellas. El método setElement() permite determinar el objeto del decorador que esta actualmente trabajando con, y getElement() es usado para recuperarlo. Cada método decorador render() acepta una cadena $content. Cuando el primer decorador es llamado, esta cadena esta tipicamente vacía, mientras las subsecuentes llamadas serán puestas. Basados en el tipo de decorador y en las opciones pasadas, el decorador ya sea reemplazará la cadena, antenpodrá la cadena o adjuntará la cadena; una separador opcional será usado en las dos últimas situaciones. Decoradores estándar Zend_Form entrega muchos decoradores estándar; ver el capítulo Decoradores estándar para detalles. Decoradores personalizados Si encuentra que sus necesidades son complejas o necesita una enorme personalización, debería considerar crear un decorador personalizado. Los decoradores necesitan implementar sólo Zend_Decorator_Interface. La interface especifica lo siguiente: Para hacerlo mas simple, simplemente puede extender Zend_Decorator_Abstract, el cual implementa todos los métodos excepto render(). Como ejemplo, digamos que quiere reducir el número de decoradores que utiliza, y construir un decorador compuesto que se encargó de renderizar la etiqueta generadora, el elemento, cualquier mensaje de error, y descripción en un div HTML. Puede construir como un decorador compuesto como sigue: getElement(); $label = $element->getLabel(); if ($translator = $element->getTranslator()) { $label = $translator->translate($label); } if ($element->isRequired()) { $label .= '*'; } $label .= ':'; return $element->getView() ->formLabel($element->getName(), $label); } public function buildInput() { $element = $this->getElement(); $helper = $element->helper; return $element->getView()->$helper( $element->getName(), $element->getValue(), $element->getAttribs(), $element->options ); } public function buildErrors() { $element = $this->getElement(); $messages = $element->getMessages(); if (empty($messages)) { return ''; } return '
' . $element->getView()->formErrors($messages) . '
'; } public function buildDescription() { $element = $this->getElement(); $desc = $element->getDescription(); if (empty($desc)) { return ''; } return '
' . $desc . '
'; } public function render($content) { $element = $this->getElement(); if (!$element instanceof Zend_Form_Element) { return $content; } if (null === $element->getView()) { return $content; } $separator = $this->getSeparator(); $placement = $this->getPlacement(); $label = $this->buildLabel(); $input = $this->buildInput(); $errors = $this->buildErrors(); $desc = $this->buildDescription(); $output = '
' . $label . $input . $errors . $desc . '
' switch ($placement) { case (self::PREPEND): return $output . $separator . $content; case (self::APPEND): default: return $content . $separator . $output; } } } ]]>
Puede entonces ubicarlo en el directorio del decorador: addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator'); // para todos los elementos: $form->addElementPrefixPath('My_Decorator', 'My/Decorator/', 'decorator'); ]]> Puede especificar este decorador como compuesto (composite) y adjuntarlo a un elemento: setDecorators(array('Composite')); ]]> Mientras este ejemplo mostró cómo crear un decorador que genera salidas complejas de muchas propiedades de elementos, puede también crear decoradores que manejen un solo aspecto de un elemento; los decoradores 'Decorator' y 'Label' son excelentes ejemplos para esta práctica. Hacerlo le permite mezclar y combinar decoradores para llegar a complejas salidas -- y también anular aspectos de decoración para personalizar sus necesidades. Por ejemplo, si quiere simplemente desplegar que un error ha ocurrido cuando validábamos un elemento, pero no desplegar individualmente cada uno de los mensajes de error, usted podría crear su propio decorador 'Errores': El valor que proporcionó no es válido; please try again'; $placement = $this->getPlacement(); $separator = $this->getSeparator(); switch ($placement) { case 'PREPEND': return $output . $separator . $content; case 'APPEND': default: return $content . $separator . $output; } } } ]]> En este ejemplo particular, debido al segmento del decorador final, 'Errors', se combina como Zend_Form_Decorator_Errors, será generado en lugar de el decorador -- significa que no necesitará cambiar ningún decorador para modificar la salida. Nombrando sus decoradores después de los decoradores existentes estándar, usted puede modificar decoradores sin necesitad de modificar sus elementos decoradores.
Generando decoradores individuales Desde que los decoradores pueden capturar distintos metadatos del elemento o formulario que ellos decoran, es a menudo útil generar un decorador individual. Afortunadamente, esta caracteristica es posible inicializando el método en cada tipo de clase form (forms, sub form, display group, element). Para hacer eso, simplemente render[DecoratorName](), cuando "[DecoratorName]" es el "nombre corto" de su decorador; opcionalmente, puede pasar en el contenido lo que usted quiera. Por ejemplo: renderLabel(); // genera sólo el campo display group, con algún contenido: echo $group->renderFieldset('fieldset content'); // genera sólo el formulario HTML, con algún contenido: echo $form->renderHtmlTag('wrap this content'); ]]> Si el decorador no existe, una excepción es inicializada. Esto puede ser útil particularmente cuando se genera un formulario con el decorador ViewScript; cada elemento puede usar sus decoradores adjuntos para generar contenido, pero con un control minucioso.
Elementos Enviados en el Formulario Estandard de Zend Framework Zend Framework viene con clases de elementos concretos cubriendo la mayoría de los elementos de los formularios HTML. La mayoría simplemente especifica una vista de ayuda para usar cuando se decora el elemento, pero varios ofrecen funcionalidad adicional. La siguiente es una lista de todas las clases, así como también una descripción de la funcionalidad que ofrecen. Zend_Form_Element_Button Usada para crear elementos HTML de tipo button, Zend_Form_Element_Button extiende Zend_Form_Element_Submit, derivandi sy funcionalidad personalizada. It specifies the 'formButton' view helper for decoration. Like the submit element, it uses the element's label as the element value for display purposes; in other words, to set the text of the button, set the value of the element. The label will be translated if a translation adapter is present. Because the label is used as part of the element, the button element uses only the ViewHelper and DtDdWrapper decorators. Después de llenar o validar un formulario, se puede verificar si el botón dado fue clickeado usando el método isChecked(). Zend_Form_Element_Captcha Los CAPTCHAs son usados para prevenir el envio automático de formularios por los robots y otros procesos automatizados. The Captcha form element allows you to specify which Zend_Captcha adapter you wish to utilize as a form captcha. It then sets this adapter as a validator to the object, and uses a Captcha decorator for rendering (which proxies to the captcha adapter). Adapters may be any adapters in Zend_Captcha, as well as any custom adapters you may have defined elsewhere. To allow this, you may pass an additional plugin loader type key, 'CAPTCHA' or 'captcha', when specifying a plugin loader prefix path: addPrefixPath('My_Captcha', 'My/Captcha/', 'captcha'); ]]> Los Captcha entonces pueden ser cargados usando el método setCaptcha(), el cual puede tomar una instancia cualquiera de captcha instance, o el nombre corto del adaptador captcha: setCaptcha(new Zend_Captcha_Figlet()); // Usando nombre corto: $element->setCaptcha('Dumb'); ]]> Si desea cargar sus elementos configuración, especifique la clave 'captcha' con un array conteniendo la clave 'captcha', o ambas claves 'captcha' y 'captchaOptions': "Please verify you're a human", 'captcha' => array( 'captcha' => 'Figlet', 'wordLen' => 6, 'timeout' => 300, ), )); // Usindo captcha y captchaOptions: $element = new Zend_Form_Element_Captcha('foo', array( 'label' => "Please verify you're a human" 'captcha' => 'Figlet', 'captchaOptions' => array( 'captcha' => 'Figlet', 'wordLen' => 6, 'timeout' => 300, ), )); ]]> El decorador usado es determinado consultando el adaptador captcha. Por defecto, es usado el Captcha decorator, pero un adaptadpr puede especificar uno diferente vía su métodogetDecorator(). Como ha notado, el adaptador captcha actúa él mismo como un validador para el elemento. Adicionalmente, el validador NotEmpty validator no es usado y el elemento es marcado como requerido. Enla mayoría de los casos, usted no necesitará hacer nada más para tener un captcha presente en su formulario. Zend_Form_Element_Checkbox Las casillas de verigicación (checkboxes) HTML le permiten devolver un valor específico, pero básicamente operata como los booleanos: cuando está marcada, el valor es enviado; cuando no está marcada, no se envía nada. Internamente, Zend_Form_Element_Checkbox forza este estado. Por defecto, si la casilla (checkbox) está marcada su valor es '1', y si no está marcada su valor es '0'. You can specify the values to use using the setCheckedValue() and setUncheckedValue() accessors, respectively. Internally, any time you set the value, if the provided value matches the checked value, then it is set, but any other value causes the unchecked value to be set. Additionally, setting the value sets the checked property of the checkbox. You can query this using isChecked() or simply accessing the property. Using the setChecked($flag) method will both set the state of the flag as well as set the appropriate checked or unchecked value in the element. Please use this method when setting the checked state of a checkbox element to ensure the value is set properly. Zend_Form_Element_Checkbox uses the 'formCheckbox' view helper. The checked value is always used to populate it. Zend_Form_Element_File The File form element provides a mechanism for supplying file upload fields to your form. It utilizes Zend_File_Transfer internally to provide this functionality, and the FormFile view helper as also the File decorator to display the form element. By default, it uses the Http transfer adapter, which introspects the $_FILES array and allows you to attach validators and filters. Validators and filters attached to the form element will be attached to the transfer adapter. File form element usage The above explanation of using the File form element may seem arcane, but actual usage is relatively trivial: setLabel('Upload an image:') ->setDestination('/var/www/upload'); // ensure only 1 file $element->addValidator('Count', false, 1); // limit to 100K $element->addValidator('Size', false, 102400); // only JPEG, PNG, and GIFs $element->addValidator('Extension', false, 'jpg,png,gif'); $form->addElement($element, 'foo'); ]]> También debe asegurarse de que se ha provisto un tipo de codificación corecto al formulario; se debe utilizar 'multipart/form-data'. Se puede hacer esto estableciendo el atributo 'enctype' en el formulario: setAttrib('enctype', 'multipart/form-data'); ]]> Cuando el atributo ha sido validado exitosamente, usted debe recibir el archivo para alamacenarlo en el destino final usando receive(). Adicionalmente puede determinar la ubicación final usando getFileName(): isValid) { print "Ohoh... validation error"; } if (!$form->foo->receive()) { print "Error receiving the file"; } $location = $form->foo->getFileName(); ]]> Ubicaciones Predeterminadas para la Carga de Archivos Por defecto, los archivos son cargados al directorio temp del sistema. Valores de archivo Dentro de HTTP un elemento file no tiene valor. Por tanto y a causa de razones de seguridad usted solo obtendrá el nombre del archivo cargado llamando a getValue() y no el destino completo. si usted necesita la información completa llame getFileName() y le retormará el destino y nombre de archivo completo. Zend_Form_Element_File soporta también archivos múltiples. Para llamar el método setMultiFile($count) usted puede establecer la cantidad de elementos file que usted desea crear. Esto le previene de establecer la misma configuración varias veces. Configuración de múltiples archivos Crear un elemento multi archivo es lo mismo que querer configurar un elemento único. Sólo tiene que llamar a setMultiFile() adicionalmente después de la creación: setLabel('Upload an image:') ->setDestination('/var/www/upload'); // asegura mínimo 1, maximo 3 archivos $element->addValidator('Count', false, array('min' => 1, 'max' => 3)); // limita a 100K $element->addValidator('Size', false, 102400); // solo JPEG, PNG, y GIFs $element->addValidator('Extension', false, 'jpg,png,gif'); // define 3 elementos file idénticos $element->setMultiFile(3); $form->addElement($element, 'foo'); ]]> En su vista usted ahora obtendrá 3 elementos para carga de archivos idénticos los cuales comparten la misma configuración. para obtener el conjunto de el número de archivos múltiples simplemente llame getMultiFile(). Elementos File en Subformularioss Cuando usted use elementos file in subformularios debería establecer nombres únicos. Así, cuando usted nombre su elemento file en el subformulario1, debe darle un nombre diferente el el subformulario2. Tan pronto como haya dos elementos file nombrados de forma idéntica, el segundo elemento no se mostrará o enviará. Para limitar el tamaño del archivo, el cual es cargado por el cliente, debe establecer el tamaño máximo de archivo que el formulario acepta . Esto limitará eñ tamaño del archivo en el lado del cliente configurando la opción MAX_FILE_SIZE en el formulario. Tan pronto como establesca este valor usando el método setMaxFileSize($size), estó será generado con el elemento file. setLabel('Upload an image:') ->setDestination('/var/www/upload') ->addValidator('Size', false, 102400) // límite en 100K ->setMaxFileSize(102400); // limita el tamaño del archivo en el lado del cliente $form->addElement($element, 'foo'); ]]> MaxFileSize con elementos file múltiples Cuando usted usa elementos file múltiples en los formularios tiene que establecer el MAX_FILE_SIZE una sola vez. Establecerlo otra vez sobreescribirá el valor previamente establecido. Note, que usted puede establecer MAX_FILE_SIZE una sola ves, incluso si usas múltiples formularios. Zend_Form_Element_Hidden Los elementos Hidden simplemente injectan datos que deben ser enviados, pero que el usuario no debe manipular. Zend_Form_Element_Hidden logra esto através del uso de el helper de vista 'formHidden'. Zend_Form_Element_Hash Este elemento provee protección de ataques desde CSRF sobre formularios, asegurando que el dato es enviado por la sesión del usuario que generó el formulario y no por un script pillero. La protección se logra mediante la adición de un elemento hash a un formulario y verificandolo cuando el formulario es enviado. El nombre del elemnto hash debe ser único. Se recomienda usar la opción salt para el elemento, dos hashes con el mismo nombre y diferentes salts no chocan: addElement('hash', 'no_csrf_foo', array('salt' => 'unique')); ]]> Puede establecer el salt más tarde usando el método setSalt($salt). Internamente, el elemento alamacena un identificador único usando Zend_Session_Namespace, y lo comprueba en el momento que se envía (comprueba que el TTL has no espiró). El validador 'Identical' entonces es usado para asegurarse que el hash enviado marcha con el hash alamacenado. El helper de vista 'formHidden' es usado par generar el elemento en el formulario. form. Zend_Form_Element_Image Las imáagenes pueden ser usadas como elmentos de formulario, y le permite especificar elementos gráficos como botones de formulario. Los elementos Image necesitan una imagen fuente. Zend_Form_Element_Image le permite especificar esto usando el método de acceso setImage() (o clave de configuración 'image'). Opcionalmente, también puede especificar un valor para utilizar al momento de enviar la imagen utilizando el método de acceso setImageValue() (o clave de configuración 'imageValue'). Cuando el valor establecido para el elemento son similares al imageValue, entonces el método de acceso isChecked() devolverá true. El elemento Image usa el Decorador de Imagen para generar (así como el estandard Errors, HtmlTag, y decorador Label). Opcionalmente, puede especificar una etiqueta para el decorador Image que luego envuelva al elemento imagen. Zend_Form_Element_MultiCheckbox A menudo tiene un conjunto de checkboxes, y desea agrupar los resultados. Esto es como un Multiselect, pero en lugar de estar en una lista desplegable, necesita mostrarlos en pares checkbox/value (casilla de verificación/valor). Zend_Form_Element_MultiCheckbox hace esto sencillo. Like all other elements extending the base Multi element, you can specify a list of options, and easily validate against that same list. The 'formMultiCheckbox' view helper ensures that these are returned as an array in the form submission. By default, this element registers an InArray validator which validates against the array keys of registered options. You can disable this behavior by either calling setRegisterInArrayValidator(false), or by passing a false value to the registerInArrayValidator configuration key. You may manipulate the various checkbox options using the following methods: addMultiOption($option, $value) addMultiOptions(array $options) setMultiOptions(array $options) (overwrites existing options) getMultiOption($option) getMultiOptions() removeMultiOption($option) clearMultiOptions() To mark checked items, you need to pass an array of values to setValue(). The following will check the values "bar" and "bat": array( 'foo' => 'Foo Option', 'bar' => 'Bar Option', 'baz' => 'Baz Option', 'bat' => 'Bat Option', ); )); $element->setValue(array('bar', 'bat')); ]]> Note that even when setting a single value, you must pass an array. Zend_Form_Element_Multiselect XHTML select elements allow a 'multiple' attribute, indicating multiple options may be selected for submission, instead of the usual one. Zend_Form_Element_Multiselect extends Zend_Form_Element_Select, and sets the multiple attribute to 'multiple'. Like other classes that inherit from the base Zend_Form_Element_Multi class, you can manipulate the options for the select using: addMultiOption($option, $value) addMultiOptions(array $options) setMultiOptions(array $options) (overwrites existing options) getMultiOption($option) getMultiOptions() removeMultiOption($option) clearMultiOptions() If a translation adapter is registered with the form and/or element, option values will be translated for display purposes. By default, this element registers an InArray validator which validates against the array keys of registered options. You can disable this behavior by either calling setRegisterInArrayValidator(false), or by passing a false value to the registerInArrayValidator configuration key. Zend_Form_Element_Password Password elements are basically normal text elements -- except that you typically do not want the submitted password displayed in error messages or the element itself when the form is re-displayed. Zend_Form_Element_Password achieves this by calling setObscureValue(true) on each validator (ensuring that the password is obscured in validation error messages), and using the 'formPassword' view helper (which does not display the value passed to it). Zend_Form_Element_Radio Radio elements allow you to specify several options, of which you need a single value returned. Zend_Form_Element_Radio extends the base Zend_Form_Element_Multi class, allowing you to specify a number of options, and then uses the formRadio view helper to display these. By default, this element registers an InArray validator which validates against the array keys of registered options. You can disable this behavior by either calling setRegisterInArrayValidator(false), or by passing a false value to the registerInArrayValidator configuration key. Like all elements extending the Multi element base class, the following methods may be used to manipulate the radio options displayed: addMultiOption($option, $value) addMultiOptions(array $options) setMultiOptions(array $options) (overwrites existing options) getMultiOption($option) getMultiOptions() removeMultiOption($option) clearMultiOptions() Zend_Form_Element_Reset Reset buttons are typically used to clear a form, and are not part of submitted data. However, as they serve a purpose in the display, they are included in the standard elements. Zend_Form_Element_Reset extends Zend_Form_Element_Submit. As such, the label is used for the button display, and will be translated if a translation adapter is present. It utilizes only the 'ViewHelper' and 'DtDdWrapper' decorators, as there should never be error messages for such elements, nor will a label be necessary. Zend_Form_Element_Select Select boxes are a common way of limiting to specific choices for a given form datum. Zend_Form_Element_Select allows you to generate these quickly and easily. By default, this element registers an InArray validator which validates against the array keys of registered options. You can disable this behavior by either calling setRegisterInArrayValidator(false), or by passing a false value to the registerInArrayValidator configuration key. As it extends the base Multi element, the following methods may be used to manipulate the select options: addMultiOption($option, $value) addMultiOptions(array $options) setMultiOptions(array $options) (overwrites existing options) getMultiOption($option) getMultiOptions() removeMultiOption($option) clearMultiOptions() Zend_Form_Element_Select uses the 'formSelect' view helper for decoration. Zend_Form_Element_Submit Submit buttons are used to submit a form. You may use multiple submit buttons; you can use the button used to submit the form to decide what action to take with the data submitted. Zend_Form_Element_Submit makes this decisioning easy, by adding a isChecked() method; as only one button element will be submitted by the form, after populating or validating the form, you can call this method on each submit button to determine which one was used. Zend_Form_Element_Submit uses the label as the "value" of the submit button, translating it if a translation adapter is present. isChecked() checks the submitted value against the label in order to determine if the button was used. The ViewHelper and DtDdWrapper decorators to render the element. No label decorator is used, as the button label is used when rendering the element; also, typically, you will not associate errors with a submit element. Zend_Form_Element_Text By far the most prevalent type of form element is the text element, allowing for limited text entry; it's an ideal element for most data entry. Zend_Form_Element_Text simply uses the 'formText' view helper to display the element. Zend_Form_Element_Textarea Textareas are used when large quantities of text are expected, and place no limits on the amount of text submitted (other than maximum size limits as dictated by your server or PHP). Zend_Form_Element_Textarea uses the 'textArea' view helper to display such elements, placing the value as the content of the element. Decoradores de Formulario (Form Decorartors) estándar contenidos en Zend Framework Zend_Form se distribuye con distintos decoradores estándar. Para más información sobre el uso de decoradores en general, vea la sección sobre decoradores. Zend_Form_Decorator_Callback El decorador Callback (llamada de retorno) permite ejecutar una llamada de retorno para mostrar el contenido. Las llamadas de retorno deben especificarse a través de la opción 'callback' pasada en la configuración del decorador, y pueden ser de cualquier valor de llamada de retorno PHP. Los Callbacks deben aceptar tres argumentos: $content (el contenido original enviado al decorador), $element (el objeto que se está decorando), y un array de $options. Un callback de ejemplo sería: ' . $element->getLabel() . ""; } } ]]> Esta llamada de retorno se especificaría como array('Util', 'label'), y generaría un (mal) código HTML para la etiqueta. El decorador Callback reemplazará, antepondrá o postpondrá el contenido original con el que devuelva. El decorador Callback permite especificar un valor null para la opción placement (colocación), que reemplazará el contenido original con el valor devuelto de la llamada de retorno; 'prepend' (anteponer) y 'append' (postponer) siguen siendo válidas. Zend_Form_Decorator_Captcha El decorador Captcha se usa junto con el elemento de formulario Captcha. Utiliza el método render() del adaptador del captcha para generar la salida. Una variante del decorador Captcha, 'Captcha_Word', es usada frecuentemente, y crea dos elementos, un id y una entrada (input). El id indica el identificador de sesión que hay que comparar, y la entrada es para la verificación de usuario del captcha. Éstos elementos son validados como un sólo elemento. Zend_Form_Decorator_Description El decorador Description puede ser usado para mostrar un conjunto de descripciones de un elemento Zend_Form, Zend_Form_Element, o Zend_Form_DisplayGroup; toma la descripción usando el método getDescription() del objeto. Por defecto, si no se encuentra la descripción, no se genera ninguna salida. Si la descripción está presente, entonces se envolverá en una etiqueta p HTML por defecto, aunque tiene la posibilidad de especificar una etiqueta pasando una opción tag al crear el decorador, o llamando a setTag(). También puede especificar una clase para el tag usando la opción class o llamando a setClass(); por defecto, se usa la clase 'hint'. La descripción es escapada utilizando los mecanismos de escapado por defecto del objeto de vista. Puede desactivar esto pasando un valor false a la opción 'escape' del decorador o el método setEscape(). Zend_Form_Decorator_DtDdWrapper Los decoradores por defecto utilizan listas de definición (<dl>) para generar elementos de formulario (form). Dato que los elementos de formulario pueden aparecer en cualquier orden, grupos de visualización y subformularios pueden ser encapsulados dentro de otros elementos de formulario. Para mantener estos tipos de elemento particulares dentro de la lista de definición, DtDdWrapper crea una nuevo término de definición vacío (definition term)(<dt>) y encapsula su contenido en un nuevo dato de definición (<dd>). La salida queda como sigue:
Información de Usuario ...
]]>
Este decorador reemplaza el contenido que se le provee envolviéndolo dentro del elemento <dd>.
Zend_Form_Decorator_Errors Los errores de elemento obtienen su propio decorador con el decorador de errores. Este decorador sustituye al view helper FormErrors, que genera mensajes de error en una lista no ordenada (<ul>) como elementos de lista (li). El elemento <ul> recibe una clase de "errores". El decorador de Errores puede anteponerse o postponerse al contenido que se le provee. Zend_Form_Decorator_Fieldset Por defecto, los grupos de visualización y subformularios generan sus contenidos dentro de fieldsets, EL decorador Fieldset busca la opción 'legend' o bien el método getLegend() en el elemento registrado, y lo usa como campo "legend" si no es vacío. Cualquier contenido pasado es envuelto en el fieldset HTML, reemplazando al contenido original. Cualquier atributo pasado al elemento decorado será generado como atributo del fieldset HTML. Zend_Form_Decorator_File Los elementos de tipo "File" (upload de ficheros) tienen una notación especial cuando se usan múltiples elementos file o subformularios. El decorador File es usado por Zend_Form_Element_File y permite fijar múltiples elementos file con una única llamada al método. Se usa automáticamente y fija el nombre de cada elemento. Zend_Form_Decorator_Form Los objetos Zend_Form normalmente necesitan generar una etiqueta HTML "form". El decorador Form utiliza la ayuda del view helper Form. Encapsula cualquier contenido provista en un elemento HTML form, usando la acción y el método del objeto Zend Form, y cualquier atributo como atributo HTML. Zend_Form_Decorator_FormElements Los formularios(forms), grupos de visualización y subformularios son colecciones de elementos. Para poder generar estos elementos, utilizan el decorador FormElements, el cual itera sobre todos los elementos, llamando a render() en cada uno de ellos y uniéndolos con el separador indicado. Puede anteponer o postponer al contenido que se le envía. Zend_Form_Decorator_FormErrors Algunos desarrolladores y diseñadores prefieren agrupar todos los mensajes de error en la parte superior del formulario. El decorador FormErrors le permite hacer esto. Por defecto, la lista de errores generada tiene el siguiente marcado:
  • [etiqueta de elemento o nombre]
    • [mensaje de error]
    • [mensaje de error]
    • [etiqueta o nombre de elemento subformulario
      • [mensaje de error]
      • [mensaje de error]
  • ]]>
    Puede pasar como parámetro varias opciones para configurar la salida generada: ignoreSubForms: se desactiva o no la recursividad en los subformularios. Por defecto: false (i.e., permitir recursividad). markupElementLabelEnd: Marcado para postponer las etiquetas de elementos. Por defecto: '</b>' markupElementLabelStart: Marcado para anteponer las etiquetas de elementos. Por defecto'<b>' markupListEnd: Marcado para postponer listas de mensajes de error. Por defecto: '</ul>'. markupListItemEnd: Marcado para postponer mensajes de error individuales. Por defecto: '</li>' markupListItemStart: Marcado para anteponer mensajes de error individuales. Por defecto: '<li>' markupListStart: Marcado para anteponer listas de mensajes de error. Por defecto: '<ul class="form-errors">' El decorador FormErrors puede anteponerse o postponerse al contenido que se le provee.
    Zend_Form_Decorator_HtmlTag El decorador HtmlTag le permite utilizar etiquetas HTML para decorador el contenido; la etiqueta utiliza es pasada en la opción 'tag' , y cualquier otra opción es usada como atributo HTML de esa etiqueta. Por defecto, el contenido generado reemplaza al contenido envolviéndolo en la etiqueta dada. De cualquier forma, se permite especificar una localización de tipo 'append' (postponer) o 'prepend' (anteponer). Zend_Form_Decorator_Image El decorador Image le permite crear un input HTML de tipo image (<input type="image" ... />), y opcionalmente mostrarlo dentro de otro tag HTML. Por defecto, el decorador usa la propiedad src del elemento, que puede fijarse con el método setImage(), como la ruta de la imagen ('src'). Adicionalmente, la etiqueta del elemento será usada como la etiqueta 'alt', y imageValue (manipulado con los métodos setImageValue() y getImageValue()) será usada como el campo 'value'. Para especificar una etiqueta HTML que utilizar con el elemento, pase la opción 'tag' al decorador, o llame explícitamente a setTag(). Zend_Form_Decorator_Label Comúnmente, los elementos de formulario tienen etiquetas (labels) y se usa el decorador Label para generar esas etiquetas. Utiliza la ayuda del view helper FormLabel, y toma la etiqueta del elemento mediante el método getLabel() de ese elemento. Si no se encuentra la etiqueta, no se genera. Por defecto, las etiquetas se traducen cuando existe un adaptador de traducciones y existe una traducción para la etiqueta. Opcionalmente, se puede especificar la opción 'tag'; si se suministra, encapsula la etiqueta en la etiqueta HTML en cuestión. Si la opción está presenta pero no hay etiqueta, la etiqueta será generada sin contenido. Puede especificar la clase que usar con la etiqueta mediante la opción 'class' o llamando a setClass(). Adicionalmente, se pueden especificar prefijos o sufijos que usar al mostrar en pantalla los elementos, basados en si la etiqueta es para un elemento opcional o requerido. Por ejemplo, podríamos querer añadir ':' a la etiqueta o un '*', indicando que el elemento es requerido. Se puede realizar con las siguientes opciones y métodos: optionalPrefix: fija el texto antepuesto a la etiqueta cuando el elemento es opcional. Utilice los accesores setOptionalPrefix() y getOptionalPrefix() para manipularlo. optionalSuffix: fija el texto pospuesto a la etiqueta cuando el elemento es opcional. Utilice los accesores setOptionalSuffix() y getOptionalSuffix() para manipularlo. requiredPrefix: fija el texto antepuesto a la etiqueta cuando el elemento es requerido. Utilice los accesores setRequiredPrefix() y getRequiredPrefix() para manipularlo. requiredSuffix: fija el texto antepuesto a la etiqueta cuando el elemento es requerido. Utilice los accesores setRequiredSuffix() y getRequiredSuffix() para manipularlo. Por defecto, el decorador Label antecede al contenido provisto; especifique la opción 'placement' (colocación) como 'append' para colocarlo después del contenido. Zend_Form_Decorator_PrepareElements Formularios, grupos de visualización, y subformularios son colecciones de elementos. Al usar el decorador ViewScript con un formulario o subformulario, resulta útil el poder fijar recursívamente el objeto de vista, el traductor (translator)y todos los nombres relacionados (determinados por la notiación de tabla del subformulario). Esta tarea puede realizarse gracias al decorador 'PrepareElements'. Normalmente, se indicará como el primer decorador en al lista. setDecorators(array( 'PrepareElements', array('ViewScript', array('viewScript' => 'form.phtml')), )); ]]> Zend_Form_Decorator_ViewHelper La mayoría de los elementos utiliza helpers Zend_View para generar el contenido, y esto se realiza con el decorador ViewHelper. Con él, se puede especificar una etiqueta 'helper' para fijar explicitamente el view helper que utilizar; si no se suministra ninguno, utiliza el último segmento del nombre de clase del elemento para determinar el helper, anteponiéndole la cadena 'form': e.g., 'Zend_Form_Element_Text' buscaría un view helper del tipo 'formText'. Cualquier atributo del elemento suministrado es pasado al view helper como atributo del elemento. Por defecto, este decorador postpone el contenido; utilice la opción 'placement' para especificar una localización distinta. Zend_Form_Decorator_ViewScript A veces es necesario usar un view script para crear elementos; De esta forma, se puede tener un control preciso sobre los elementos; entregar el view script a un diseñador, o simplemente crear una forma fácil de sobreescribir basado en el módulo que se esté usando. El decorador ViewScript soluciona este problema. El decorador ViewScript requiere una opción 'viewScript', o bien suministrada al decorador, o bien como atributo del elemento. Entonces genera ese script de vista como un script parcial, lo que significa que cada llamada a él tiene su propio espacio de variables; Ninguna variable de la vista será rellenada, aparte del elemento en sí. Distintas variables son entonces rellenadas: element: el elemento decorado content: el contenido pasado al decorador decorator: el propio objeto decorador Del mismo modo, todas las opciones pasadas al decorador a través de setOptions() que no son usadas internamente (tales como placement, separator, etc.) son pasadas como variables de vista. Como ejemplo, se pueden tener el siguiente elemento: setDecorators(array(array('ViewScript', array( 'viewScript' => '_element.phtml', 'class' => 'form element' )))); // o especificando el viewScript como un atributo del elemento: $element->viewScript = '_element.phtml'; $element->setDecorators(array(array('ViewScript', array('class' => 'form element')))); ]]> Un view script puede tener el siguiente aspecto: class ?>"> formLabel($this->element->getName(), $this->element->getLabel()) ?> {$this->element->helper}( $this->element->getName(), $this->element->getValue(), $this->element->getAttribs() ) ?> formErrors($this->element->getMessages()) ?>
    element->getDescription() ?>
    ]]>
    Reemplazar contenido con un script de vista (view script) Resulta interesante que el script de vista reemplace el contenido provisto por el decorador -- por ejemplo, si desea encapsularlo. Puede hacer esto especificando un valor booleano false en la opción 'placement' del decorador: addDecorator('ViewScript', array('placement' => false)); // Aplicado a una instancia de un decorador ya existente: $decorator->setOption('placement', false); // Aplicado a un decorador ya asociado a un elemento: $element->getDecorator('ViewScript')->setOption('placement', false); // Dentro de un view script usado por un decorador: $this->decorator->setOption('placement', false); ]]> Se recomienda usar el decorador ViewScript cuando necesite un control muy preciso sobre cómo generados sus elementos.
    Uso avanzado de Zend_Form Zend_Form tiene una riqueza de funcionalidad, has a wealth of functionality, muchas de ellas diregidas a expertos desarroladores. Este capítulo tiene por objeto al documento algunas de estas funcionalidades con ejemplos y casos de uso. Notación de array Muchos desarroladores experimentados les gusta agrupar elementos de formulario usando notación de array en los nombres de elementos. Por ejemplo, si se tiene dos direcciones que se desea capturar, un envio y una dirección de facturación, se puede tener elementos identicos; agrupandolos en un array se puede asegurar que son capturados por separado. Toma el siguiente fomrulario por ejemplo:
    Shipping Address
    Billing Address
    ]]>
    En este ejemplo, la facturación y la dirección de envío contienen algunos campos identicos, eso significa uno puede sobre escribir al otro. Nosotros podemos resolver esta solución usando una notación de array:
    Shipping Address
    Billing Address
    ]]>
    In the above sample, we now get separate addresses. In the submitted form, we'll now have three elements, the 'save' element for the submit, and then two arrays, 'shipping' and 'billing', each with keys for their various elements. Zend_Form attempts to automate this process with its sub forms. By default, sub forms render using the array notation as shown in the previous HTML form listing, complete with ids. The array name is based on the sub form name, with the keys based on the elements contained in the sub form. Sub forms may be nested arbitrarily deep, and this will create nested arrays to reflect the structure. Additionally, the various validation routines in Zend_Form honor the array structure, ensuring that your form validates correctly, no matter how arbitrarily deep you nest your sub forms. You need do nothing to benefit from this; this behaviour is enabled by default. Additionally, there are facilities that allow you to turn on array notation conditionally, as well as specify the specific array to which an element or collection belongs: Zend_Form::setIsArray($flag): By setting the flag true, you can indicate that an entire form should be treated as an array. By default, the form's name will be used as the name of the array, unless setElementsBelongTo() has been called. If the form has no specified name, or if setElementsBelongTo() has not been set, this flag will be ignored (as there is no array name to which the elements may belong). You may determine if a form is being treated as an array using the isArray() accessor. Zend_Form::setElementsBelongTo($array): Using this method, you can specify the name of an array to which all elements of the form belong. You can determine the name using the getElementsBelongTo() accessor. Additionally, on the element level, you can specify individual elements may belong to particular arrays using Zend_Form_Element::setBelongsTo() method. To discover what this value is -- whether set explicitly or implicitly via the form -- you may use the getBelongsTo() accessor.
    Multi-Page Forms Currently, Multi-Page forms are not officially supported in Zend_Form; however, most support for implementing them is available and can be utilized with a little extra tooling. The key to creating a multi-page form is to utilize sub forms, but to display only one such sub form per page. This allows you to submit a single sub form at a time and validate it, but not process the form until all sub forms are complete. Registration Form Example Let's use a registration form as an example. For our purposes, we want to capture the desired username and password on the first page, then the user's metadata -- given name, family name, and location -- and finally allow them to decide what mailing lists, if any, they wish to subscribe to. First, let's create our own form, and define several sub forms within it: addElements(array( new Zend_Form_Element_Text('username', array( 'required' => true, 'label' => 'Username:', 'filters' => array('StringTrim', 'StringToLower'), 'validators' => array( 'Alnum', array('Regex', false, array('/^[a-z][a-z0-9]{2,}$/')) ) )), new Zend_Form_Element_Password('password', array( 'required' => true, 'label' => 'Password:', 'filters' => array('StringTrim'), 'validators' => array( 'NotEmpty', array('StringLength', false, array(6)) ) )), )); // Create demographics sub form: given name, family name, and // location $demog = new Zend_Form_SubForm(); $demog->addElements(array( new Zend_Form_Element_Text('givenName', array( 'required' => true, 'label' => 'Given (First) Name:', 'filters' => array('StringTrim'), 'validators' => array( array('Regex', false, array('/^[a-z][a-z0-9., \'-]{2,}$/i')) ) )), new Zend_Form_Element_Text('familyName', array( 'required' => true, 'label' => 'Family (Last) Name:', 'filters' => array('StringTrim'), 'validators' => array( array('Regex', false, array('/^[a-z][a-z0-9., \'-]{2,}$/i')) ) )), new Zend_Form_Element_Text('location', array( 'required' => true, 'label' => 'Your Location:', 'filters' => array('StringTrim'), 'validators' => array( array('StringLength', false, array(2)) ) )), )); // Create mailing lists sub form $listOptions = array( 'none' => 'No lists, please', 'fw-general' => 'Zend Framework General List', 'fw-mvc' => 'Zend Framework MVC List', 'fw-auth' => 'Zend Framwork Authentication and ACL List', 'fw-services' => 'Zend Framework Web Services List', ); $lists = new Zend_Form_SubForm(); $lists->addElements(array( new Zend_Form_Element_MultiCheckbox('subscriptions', array( 'label' => 'Which lists would you like to subscribe to?', 'multiOptions' => $listOptions, 'required' => true, 'filters' => array('StringTrim'), 'validators' => array( array('InArray', false, array(array_keys($listOptions))) ) )), )); // Attach sub forms to main form $this->addSubForms(array( 'user' => $user, 'demog' => $demog, 'lists' => $lists )); } } ]]> Note that there are no submit buttons, and that we have done nothing with the sub form decorators -- which means that by default they will be displayed as fieldsets. We will need to be able to override these as we display each individual sub form, and add in submit buttons so we can actually process them -- which will also require action and method properties. Let's add some scaffolding to our class to provide that information: {$spec}; } elseif ($spec instanceof Zend_Form_SubForm) { $subForm = $spec; } else { throw new Exception('Invalid argument passed to ' . __FUNCTION__ . '()'); } $this->setSubFormDecorators($subForm) ->addSubmitButton($subForm) ->addSubFormActions($subForm); return $subForm; } /** * Add form decorators to an individual sub form * * @param Zend_Form_SubForm $subForm * @return My_Form_Registration */ public function setSubFormDecorators(Zend_Form_SubForm $subForm) { $subForm->setDecorators(array( 'FormElements', array('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')), 'Form', )); return $this; } /** * Add a submit button to an individual sub form * * @param Zend_Form_SubForm $subForm * @return My_Form_Registration */ public function addSubmitButton(Zend_Form_SubForm $subForm) { $subForm->addElement(new Zend_Form_Element_Submit( 'save', array( 'label' => 'Save and continue', 'required' => false, 'ignore' => true, ) )); return $this; } /** * Add action and method to sub form * * @param Zend_Form_SubForm $subForm * @return My_Form_Registration */ public function addSubFormActions(Zend_Form_SubForm $subForm) { $subForm->setAction('/registration/process') ->setMethod('post'); return $this; } } ]]> Next, we need to add some scaffolding in our action controller, and have several considerations. First, we need to make sure we persist form data between requests, so that we can determine when to quit. Second, we need some logic to determine what form segments have already been submitted, and what sub form to display based on that information. We'll use Zend_Session_Namespace to persist data, which will also help us answer the question of which form to submit. Let's create our controller, and add a method for retrieving a form instance: _form) { $this->_form = new My_Form_Registration(); } return $this->_form; } } ]]> Now, let's add some functionality for determining which form to display. Basically, until the entire form is considered valid, we need to continue displaying form segments. Additionally, we likely want to make sure they're in a particular order: user, demog, and then lists. We can determine what data has been submitted by checking our session namespace for particular keys representing each subform. _session) { $this->_session = new Zend_Session_Namespace($this->_namespace); } return $this->_session; } /** * Get a list of forms already stored in the session * * @return array */ public function getStoredForms() { $stored = array(); foreach ($this->getSessionNamespace() as $key => $value) { $stored[] = $key; } return $stored; } /** * Get list of all subforms available * * @return array */ public function getPotentialForms() { return array_keys($this->getForm()->getSubForms()); } /** * What sub form was submitted? * * @return false|Zend_Form_SubForm */ public function getCurrentSubForm() { $request = $this->getRequest(); if (!$request->isPost()) { return false; } foreach ($this->getPotentialForms() as $name) { if ($data = $request->getPost($name, false)) { if (is_array($data)) { return $this->getForm()->getSubForm($name); break; } } } return false; } /** * Get the next sub form to display * * @return Zend_Form_SubForm|false */ public function getNextSubForm() { $storedForms = $this->getStoredForms(); $potentialForms = $this->getPotentialForms(); foreach ($potentialForms as $name) { if (!in_array($name, $storedForms)) { return $this->getForm()->getSubForm($name); } } return false; } } ]]> The above methods allow us to use notations such as "$subForm = $this->getCurrentSubForm();" to retrieve the current sub form for validation, or "$next = $this->getNextSubForm();" to get the next one to display. Now, let's figure out how to process and display the various sub forms. We can use getCurrentSubForm() to determine if any sub forms have been submitted (false return values indicate none have been displayed or submitted), and getNextSubForm() to retrieve a form to display. We can then use the form's prepareSubForm() method to ensure the form is ready for display. When we have a form submission, we can validate the sub form, and then check to see if the entire form is now valid. To do these tasks, we'll need additional methods that ensure that submitted data is added to the session, and that when validating the form entire, we validate against all segments from the session: getName(); if ($subForm->isValid($data)) { $this->getSessionNamespace()->$name = $subForm->getValues(); return true; } return false; } /** * Is the full form valid? * * @return bool */ public function formIsValid() { $data = array(); foreach ($this->getSessionNamespace() as $key => $info) { $data[$key] = $info; } return $this->getForm()->isValid($data); } } ]]> Now that we have the legwork out of the way, let's build the actions for this controller. We'll need a landing page for the form, and then a 'process' action for processing the form. getCurrentSubForm()) { $form = $this->getNextSubForm(); } $this->view->form = $this->getForm()->prepareSubForm($form); } public function processAction() { if (!$form = $this->getCurrentSubForm()) { return $this->_forward('index'); } if (!$this->subFormIsValid($form, $this->getRequest()->getPost())) { $this->view->form = $this->getForm()->prepareSubForm($form); return $this->render('index'); } if (!$this->formIsValid()) { $form = $this->getNextSubForm(); $this->view->form = $this->getForm()->prepareSubForm($form); return $this->render('index'); } // Valid form! // Render information in a verification page $this->view->info = $this->getSessionNamespace(); $this->render('verification'); } } ]]> As you'll notice, the actual code for processing the form is relatively simple. We check to see if we have a current sub form submission, and if not, we go back to the landing page. If we do have a sub form, we attempt to validate it, redisplaying it if it fails. If the sub form is valid, we then check to see if the form is valid, which would indicate we're done; if not, we display the next form segment. Finally, we display a verification page with the contents of the session. The view scripts are very simple:

    Registration

    form ?>

    Thank you for registering!

    Here is the information you provided:

    info as $info): foreach ($info as $form => $data): ?>

    :

    $value): ?>
    $val): ?>
    escape($value) ?>
    ]]>
    Upcoming releases of Zend Framework will include components to make multi page forms simpler by abstracting the session and ordering logic. In the meantime, the above example should serve as a reasonable guideline on how to accomplish this task for your site.
    Zend_Gdata Zend_Http Zend_InfoCard Zend_Json Zend_Layout Introducción Zend_Layout implementa un patrón clásico "Vista en dos etapas" (Two Step View) permitiendo a los desarrolladores colocar el contenido de la aplicación dentro de otra vista, usualmente representando la plantilla del sitio. Tales plantillas son a menudo denominadas layouts por otros proyectos, y Zend Framework ha adoptado este término por consistencia. Los objetivos principales de Zend_Layout son los siguientes: Automatizar la selección y renderizado de layouts cuando se usan con los componentes MVC de Zend Framework. Proveer ámbitos separados para variables relacionadas al diseño y contenido. Permitir configuraciones, incluyendo el nombre del layout, resolución (inflexión) del script layout, y ruta del script layout. Permitir deshabilitar layouts, cambiar el script de diseño y otras condiciones; permitir estas acciones dentro de los controladores y scripts de vista. Seguir normas de resolución similares (inflexión) como el ViewRenderer, pero permitiendo también el uso de normas distintas Permitir el uso de los componentes MVC de Zend Framework. Zend_Ldap Zend_Loader Cargando archivos y clases dinámicamente La clase Zend_Loader incluye métodos para ayudar a cargar archivos dinámicamente. Zend_Loader vs. require_once() Los métodos de Zend_Loader tienen más utilidad si el nombre de archivo que necesita cargar es variable. Por ejemplo, si éste se basa en un parametro de entrada del usuario o argumento de un método. Si carga un archivo o clase cuyo nombre es constante, no hay ningún beneficio al usar Zend_Loader sobre el uso de funciones tradicionales de PHP como require_once(). Cargando Archivos El método estático Zend_Loader::loadFile() carga un archivo PHP. El archivo cargado puede contener cualquier código PHP. El método se comporta como un envoltorio para la función PHP include(). Este método devuelve un booleano false en caso de fallo, por ejemplo, si el archivo especificado no existe. Ejemplo del Método loadFile() El argumento $filename especifica el archivo que se va a cargar, el cual no debe contener ninguna información de rutas. Una verificación de seguridad es efectuada sobre $filename. El archivo $filename sólo puede contener caracteres alfanuméricos, guiones ("-"), barras bajas ("_"), o puntos ("."). No hay ninguna restricción en el argumento $dirs. El parámetro $dirs especifica en qué carpetas buscar el archivo. Si el valor es NULL, sólo se buscará en el include_path ; si el valor es un string o un array, se buscará en la carpeta o carpetas especificadas , seguidas del include_path. El argumento $once es un booleano. Si es TRUE, Zend_Loader::loadFile() esa la función PHP include_once() para cargar el archivo, de lo contrario se utiliza la función PHP include(). Cargando Clases El método estático Zend_Loader::loadClass($class, $dirs) carga un archivo PHP y comprueba la existencia de la clase. Ejemplo del método loadClass() La cadena que especifica la clase es convertida a una ruta relativa sustituyendo las barras bajas (_) por el separador de carpeta de su Sistema Operativo, y añadiendo '.php'. En el ejemplo de arriba, 'Container_Tree' se convierte en 'Container/Tree.php' en Windows. Si $dirs es una cadena o un array, Zend_Loader::loadClass() busca las carpetas en el orden suministrado. El primer archivo encontrado es cargado. Si el archivo no existe en el $dirs especificado, entonces se busca en el include_path del entorno PHP. Si el archivo no es encontrado o la clase no existe después de la carga, Zend_Loader::loadClass() lanza una Zend_Exception. Zend_Loader::loadFile() se usa para cargar, así que el nombre de la clase puede contener únicamente caracteres alfanuméricos, guiones ('-'), barras bajas ('_'), y puntos ('.'). Comprobando si un Archivo Puede Ser Leído El método estático Zend_Loader::isReadable($pathname) devuelve TRUE si el archivo en la ruta $pathname existe y tiene permisos de lectura, FALSE en caso contrario. Ejemplo del método isReadable() El argumento $filename especifica el nombre de archivo que comprobar. Puede contener información de la ruta. Este método envuelve la función PHP is_readable(). La función PHP no busca en include_path, mientras que Zend_Loader::isReadable() sí. Usando el Autoloader La clase Zend_Loader contiene un método que se puede registrar con PHP SPL autoloader. Zend_Loader::autoload() es el método callback. Por comodidad, Zend_Loader permite a la función registerAutoload() registrar su método autoload(). Si la extensión spl_autoload no está presente en el entorno PHP, entonces el método registerAutoload() lanza una Zend_Exception. Ejemplo de registro del método callback del autoloader Después de registrar el callback de autoload de Zend Framework, se pueden referenciar clases de Zend Framework sin tener que cargarlas explícitamente. El método autoload() usa automáticamente Zend_Loader::loadClass() cuando referencie una clase. Si ha extendido la clase Zend_Loader, se puede pasar un argumento opcional a registerAutoload(), para especificar la clase a partir de la cual registrar un método autoload(). Ejemplo de registro del método de callback autoload desde una clase extendida Debido a la semántica de referencia de funciones estáticas en PHP, se debe implementar código tanto para la clase loadClass() como autoload(), y autoload() debe llamar a self::loadClass(). Si su método autoload() delega en su padre la llamada a self::loadClass(), entonces llamará al método con ese nombre en la clase padre, no la subclase. Se puede eliminar un callback de autoload. registerAutoload() tiene un segundo parámetro opcional, que es true por defecto. Si este parámetro es false, el callback de autoload será borrado de la pila de autoload SPL. Zend_Locale Zend_Log Zend_Mail Zend_Measure Zend_Memory Zend_Mime Zend_OpenId Zend_Paginator Zend_Pdf Zend_ProgressBar Zend_Registry Zend_Rest Zend_Search_Lucene Zend_Server Zend_Service Zend_Session Zend_Soap Zend_Test Zend_Text Zend_TimeSync Zend_Translate Zend_Uri Zend_Validate Introducción Cuando se necesita validar algún tipo de dato, el componente Zend_Validate ofrece un conjunto de validadores, como así también un sencillo mecanismo de encadenado de validaciones por el cual múltiples validadores pueden aplicarse a un dato en un orden definido por el usuario. ¿Qué es un validador? Un validador examina su entrada con respecto a algunos requerimientos y produce un resultado booleano si la entrada valida satisfactoriamente con los requisitos. Si la entrada no cumple los requisitos, un validador también podrá proporcionar información adicional sobre que requisito(s) no son satisfechos. Por ejemplo, una aplicación web podría requerir que un usuario ingrese su nombre, de entre seis y doce caracteres de longitud y que sólo puede contener caracteres alfanuméricos. Se puede usar un validador para asegurar que los usuarios cumplan estos requisitos. Si el nombre de usuario elegido no cumple con uno o ambos de los requisitos, sería útil saber cuál de estos requisitos no se cumple. Uso básico de validadores Habiendo definido la validación de esta manera, Zend Framework nos proporciona el fundamento para Zend_Validate_Interface que define dos métodos, isValid() y getMessages(). El método isValid() realiza la validación del valor, devolviendo true si y sólo si el valor pasa contra el criterio de validación. Si isValid() devuelve false, la función getMessages() devuelve un array de mensajes explicando el motivo(s) del fracaso de la validación. Las claves del array son strings cortos que identifican las razones por las cuales fracasó la validación, y los valores del array son los correspondientes mensajes para ser leídos por un ser humano. Las claves y los valores son dependientes de la clase; cada clase de validación define su propio conjunto de mensajes de validación fallidas y las claves únicas que las identifican. Cada clase tiene también una definición const que hace corresponder a cada identificador con una causa del fallo de validación. El método getMessages() devuelve información del fracaso de la validación sólo para la llamada más reciente a isValid(). Cada llamada a isValid() borra los mensajes y errores causados por una llamada anterior isValid(), porque es probable que cada llamada a isValid() se refiera al valor de una entrada diferente. El siguiente ejemplo ilustra la validación de una dirección de e-mail: isValid($email)) { // email parece ser válido } else { // email es inválido; muestre la razones foreach ($validator->getMessages() as $messageId => $message) { echo "Falla de validación '$messageId': $message\n"; } } ]]> Personalizar los mensajes Para validar las clases se proporciona un método setMessage() con el que se puede especificar el formato de un mensaje devuelto por getMessages() en caso de fallo de validación. El primer argumento de este método es un string que contiene el mensaje de error. Usted puede incluir tokens en este array que serán sustituidos con datos relevantes al validador. El token %value% es aceptado por todos los validadores, que es sustituido por el valor que pasó a isValid(). Cada clase de validación, puede dar apoyo a otros tokens en base a cada caso. Por ejemplo, %max% es un token apoyado por Zend_Validate_LessThan. El método getMessageVariables() devuelve un array de tokens variables aceptados por el validador. El segundo argumento opcional es un string que identifica la plantilla de mensajes que se establecerá en caso del fracaso de la validación, lo que es útil cuando una clase de validación define más de una causa para el fallo. Si omite el segundo argumento, setMessage() asume que el mensaje que especifique debe ser utilizado por la plantilla del primer mensaje que declaró en la clase de validación. Muchas clases de validación sólo definen una plantilla de mensaje de error, así que no hay necesidad de especificar el cambio de plantilla de mensaje. setMessage( 'El string \'%value%\' es muy corto; debe tener al menos %min% ' . 'caracteres', Zend_Validate_StringLength::TOO_SHORT); if (!$validator->isValid('word')) { $messages = $validator->getMessages(); echo current($messages); // "El string 'word' es muy corto; debe tener al menos 8 caracteres" } ]]> Puede establecer varios mensajes usando el método setMessages(). Su argumento es un array que contiene pares de clave/mensaje. setMessages( array( Zend_Validate_StringLength::TOO_SHORT => 'El string \'%value%\' es muy corto', Zend_Validate_StringLength::TOO_LONG => 'El string \'%value%\' es muy largo' )); ]]> Incluso, si su aplicación requiere una mayor flexibilidad para informar los fallos de validación, puede acceder a las propiedades por el mismo nombre, tal como los tokens de mensajes apoyados por una determinada clase de validación. La propiedad value siempre está disponible en un validador; es el valor que especificó en el argumento de isValid(). En cada clase de validación se puede dar apoyo a otras propiedades basándose en el esquema de caso por caso. isValid('word')) { echo 'Palabra fallada: ' . $validator->value . '; su longitud no está entre ' . $validator->min . ' y ' . $validator->max . "\n"; } ]]> Utilizando el método estático <code>is()</code> Si es inconveniente cargar una clase de validación y crear una instancia del validador, puede usar el método estático Zend_Validate::is() como un estilo alternativo de invocación. El primer argumento de este método es el valor de una entrada de datos que usted pasaría al método isValid(). El segundo argumento es un string, que corresponde al nombre base de la clase de validación, relativo al nombre de espacio Zend_Validate. El método is() carga automáticamente la clase, crea una instancia y aplica el método isValid() a la entrada de datos. Si el validador lo necesita, también puede pasar un array de constructores de argumentos. El método is() devuelve un valor booleano, lo mismo que el método isValid(). Cuando se utiliza el método estático is(), no están disponibles los mensajes de fracaso de validación. El uso estático puede ser conveniente para invocar un validador ad-hoc (hecho especialmente), pero si tiene la necesidad de ejecutar el validador para múltiples entradas, es más eficiente usar métodos no estáticos, creando una instancia del objeto validador y llamando a su método isValid(). También la clase Zend_Filter_Input le permite crear ejemplos y ejecutar múltiples filtros y clases de validadores por demanda, para procesar juegos de datos de entrada. Ver . Clases de Validación Estándar Zend Framework viene con un conjunto estándar de clases de validación listas para usar. Alnum Devuelve true si y sólo si $valor contiene caracteres alfanuméricos únicamente. Este validador incluye una opción para considerar también al espacio en blanco como caracter válido. Alpha Devuelve true si y sólo si $valor sólo contiene caracteres alfabéticos. Este validador incluye una opción para considerar también al espacio en blanco como caracter válido. Barcode Este validador es instanciado con un tipo de código de barras contra el valor del código de barras que quiere validar. En la actualidad acepta los tipos de código de barras "UPC-A" (Universal Product Code) y "EAN-13" (European Article Number), además el método isValid() devuelve verdadero si y solo si la entrada valida satisfactoriamente contra el algoritmo de validación del código de barras. Antes de enviar los datos de entrada al validador, debe asegurarse de eliminar todos los caracteres distintos a los dígitos cero a nueve (0-9). Between Devuelve true si y sólo si $valor está entre los valores límites mínimo y máximo. La comparación es inclusiva por defecto ($valor puede ser igual a una valor límite), aunque esto puede ser anulado a fin de hacer una comparación estricta, donde $valor debe ser estrictamente mayor al mínimo y estrictamente menor al máximo. Ccnum Devuelve true si y sólo si $valor sigue el algoritmo Luhn (mod-10 checksum) para tarjetas de crédito. Date Devuelve true si y sólo si $valor es una fecha válida en el formato YYYY-MM-DD (AAAA-MM-DD). Si se usa la opción locale entonces la fecha será validada de acuerdo a lo establecido para ese lugar. El formato format es una opción que establece este formato a ser utilizado para la validación. Para los detalles acerca de los parámetros opcionales ver en: Zend_Date::isDate(). Digits Devuelve true si y sólo si $valor contiene solamente dígitos. Float Devuelve true si y sólo si $valor es un valor de punto flotante. GreaterThan Devuelve true si y sólo si $valor es mayor al límite mínimo. Hex Devuelve true si y sólo si $valor contiene caracteres hexadecimales (0-9 y A-F). Hostname (Nombre de Host) Zend_Validate_Hostname le permite validar un nombre de host contra una serie de especificaciones conocidas. Es posible comprobar por tres diferentes tipos de nombres: el DNS Hostname (domain.com por ejemplo), dirección IP (es decir 1.2.3.4), y nombres de host locales (localhost, por ejemplo). Por defecto sólo se comprobarán nombres de host DNS. Uso básico El siguiente es un ejemplo de uso basico: isValid($hostname)) { // hostname parece ser válido } else { // hostname es inválido; muestre las razones foreach ($validator->getMessages() as $message) { echo "$message\n"; } } ]]> Comprobará el nombre de host $hostname y si fracasa alimentará a $validator->getMessages() con mensajes de error. Validar diferentes tipos de nombres de host También se puede encontrar coincidencias de direcciones IP, nombres de host locales, o una combinación de todos los tipos permitidos. Esto puede hacerse pasando un parámetro a Zend_Validate_Hostname cuando lo instancia. El parámetro debe ser un entero que determina qué tipos de nombres de host están permitidos. Se recomienda el uso de las constantes de Zend_Validate_Hostname para hacerlo. Las constantes de Zend_Validate_Hostname son: ALLOW_DNS para permitir sólo nombres de host DNS, ALLOW_IP para permitir direcciones IP, ALLOW_LOCAL para permitir nombres de host de la red local, y ALLOW_ALL para permitir todos estos tres tipos. Para comprobar que direcciones IP puede utilizar, vea el siguiente ejemplo: isValid($hostname)) { // hostname parece ser válido } else { // hostname es inválido; muestre las razones foreach ($validator->getMessages() as $message) { echo "$message\n"; } } ]]> Usando ALLOW_ALL para aceptar todos los tipos de nombres de host, también puede combinar estos tipos para realizar combinaciones. Por ejemplo, para aceptar nombres de host DNS y locales, instancie el objeto Zend_Validate_Hostname como: Validación de Nombres de Dominio Internacionales Algunos (ccTLD), es decir países "Country Code Top Level Domains" , como 'de' (Alemania), aceptan caracteres internacionales como nombres de dominio. Estos son conocidos como Nombres de Dominio Internacionales (IDN, por sus siglas en inglés). Se puede buscar una coincidencia de estos dominios con Zend_Validate_Hostname, a través de caracteres extendidos que se utilizan en el proceso de validación. En la actualidad la lista de las ccTLDs incluyen a: at (Austria) ch (Suiza) li (Liechtenstein) de (Alemania) fi (Finlandia) hu (Hungría) no (Noruega) se (Suecia) Cotejar dominios IDN es tan simple como usar el validador estándar Hostname, ya que este viene habilitado por defecto. Si desea desactivar la validación IDN, se puede hacer ya sea pasando un parámetro al constructor Zend_Validate_Hostname o a través del método $validator->setValidateIdn(). Puede deshabilitar la validación IDN, pasando un segundo parámetro al constructor Zend_Validate_Hostname de la siguiente manera. Alternativamente puede pasar TRUE o FALSE a $validator->setValidateIdn() para activar o desactivar la validación IDN. Si está tratando de cotejar un nombre de host IDN que actualmente no está soportado, es probable que falle la validación si tiene caracteres internacionales en el nombre de host. Cuando un archivo ccTLD no existe en Zend/Validate/Hostname, especificando los caracteres adicionales se puede realizar una validación normal. Tenga en cuenta que una validación IDN solo se realizará si tiene habilidada la validación para nombres de host DNS. Validar dominios de nivel superior Por defecto un nombre de host se cotejará con una lista de TLDs conocidos. Si esta funcionalidad no es necesaria, puede ser desactivada en la misma forma que deshabilita el soporte IDN. Puede deshabilitar la validación TLD pasando un tercer parámetro al constructor Zend_Validate_Hostname. En el siguiente ejemplo estamos dando respaldo a la validación IDN a través del segundo parámetro. Alternativamente puede pasar TRUE o FALSE a $validator->setValidateTld() para activar o desactivar la validación TLD. Tenga en cuenta que una validación de TLDs solo se realizará si tiene habilidada la validación para nombres de host DNS. InArray Devuelve true si y sólo si $valor se encuentra en un array, y si la opción es estricta entonces también verificará el tipo de dato de $valor. Int Devuelve true si y sólo si $valor es un valor entero válido. Ip Devuelve true si y sólo si $valor es una dirección IP válida. LessThan Devuelve true si y sólo si $valor es menor al límite máximo. NotEmpty Devuelve true si y sólo si $valor no es vacío. Regex Devuelve true si y sólo si $valor coincide con el patrón de una expresión regular. StringLength Devuelve true si y sólo si la longitud del string $valor es por lo menos un mínimo y no mayor a un máximo (cuando la opción max no es null). Desde la versión 1.5.0, el método setMin() lanza una excepción si la longitud mínima tiene un valor mayor que la longitud máxima establecida, y el método setMax() lanza una excepción si la longitud máxima se fija a un valor inferior que la longitud mínima establecida. Desde la versión 1.0.2, esta clase soporta UTF-8 y a otras codificaciones, basado en el valor actual de: iconv.internal_encoding. Cadenas de Validadores Frecuentemente deben aplicarse múltiples validaciones a algún valor en un orden particular. El siguiente código demuestra una forma de resolver el ejemplo de la introducción, donde el nombre de usuario debe tener entre 6 y 12 caracteres alfanuméricos. addValidator(new Zend_Validate_StringLength(6, 12)) ->addValidator(new Zend_Validate_Alnum()); // Valida el username if ($validatorChain->isValid($username)) { // username pasó la validación } else { // username falló en la validación; muestre las razones foreach ($validatorChain->getMessages() as $message) { echo "$message\n"; } } ]]> Los validadores se ejecutan en el orden en que se agregaron a Zend_Validate. En el ejemplo anterior, el nombre de usuario, primero se comprueba que su longitud esté entre 6 y 12 caracteres y luego se controla para garantizar que sólo contiene caracteres alfanuméricos. La segunda validación; de caracteres alfanuméricos; se realiza independientemente de que la primera validación; de longitud entre 6 y 12 caracteres; tenga éxito. Esto significa que si ambas validaciones fallan, getMessages() devolverá mensajes de fracaso desde ambos validadores. En algunos casos tiene sentido detener la cadena de validación si falla alguno de los procesos de validación. Zend_Validate acepta tales casos pasando como segundo parámetro el método addValidator(). Poniendo $breakChainOnFailure a true, el validador agregado quebrará la cadena de ejecución por el fracaso, que evita correr cualquier otra validación que se decida que es innecesaria o inapropiada para la situación. Si el ejemplo anterior fue escrito como sigue, entonces el sistema de validación alfanumérica no se ejecutará si falla la longitud del string de validación: addValidator(new Zend_Validate_StringLength(6, 12), true) ->addValidator(new Zend_Validate_Alnum()); ]]> Cualquier objeto que implemente Zend_Validate_Interface puede ser utilizado en una cadena de validación. Escribiendo Validadores Zend_Validate provee un conjunto de validadores que suelen necesitarse, pero inevitablemente, los desarrolladores quiere escribir sus propios validadores personalizados para sus necesidades particulares. La tarea de escribir un validador personalizado se describe en esta sección. Zend_Validate_Interface define tres métodos, isValid(), getMessages(), y getErrors(), que pueden ser implementadas por clases de usuario a fin de crear objetos de validación personalizados. Un objeto que implementa una interfaz Zend_Validate_Interface puede añadirse a una cadena de validación con Zend_Validate::addValidator(). Tales objetos también pueden ser usados con Zend_Filter_Input. De la descripción anterior de Zend_Validate_Interface, podrá inferir que las clases de validación que proporciona Zend Framework devuelven un valor booleano para cuando un valor se valida satisfactoriamente o no. También proporcionan información sobre por qué un valor falló en la validación. La disponibilidad de las razones para los fracasos de validación puede ser valiosa para una aplicación por diversos motivos, tales como proporcionar estadísticas para análisis de usabilidad. La funcionalidad de los mensajes de validación básica de fallos están implementados en Zend_Validate_Abstract. A fin de incluir esta funcionalidad al crear una clase de validación, simplemente extienda Zend_Validate_Abstract. En la extensión de la clase deberá aplicar la lógica del método isValid() y definir las variables y plantillas de mensajes que correspondan a los tipos de fallos de validación que puedan suceder. Si falla un valor en su test de validación, entonces isValid() deberá devolver false. Si el valor pasa su test de validación, entonces isValid() deberá devolver true. En general, el método isValid() no debería arrojar excepciones, salvo que sea imposible determinar si el valor de entrada es válido o no. Algunos ejemplos de casos razonables para lanzar una excepción podría ser si un archivo no puede abrirse, que un servidor LDAP no pudiera ser contactado, o una conexión a una base de datos no estuviera disponible. Estos son casos en los que puede ser necesario determinar el éxito o fracaso de la validación. Crear una Clase de Validación sencilla El siguiente ejemplo demuestra cómo podría escribirse un sencillo validador personalizado. En este caso las reglas de validación son simplemente que el valor de entrada debe ser de punto flotante. "'%value%' no es un valor de punto flotante" ); public function isValid($value) { $this->_setValue($value); if (!is_float($value)) { $this->_error(); return false; } return true; } } ]]> La clase define una plantilla para su único mensaje de fallo de validación, que incluye el mágico parámetro %value%. La llamada a _setValue() prepara al objeto para insertar automáticamente en el mensaje de fallo al valor probado, si éste falla en la validación. La llamada a _error() sigue las pistas para establecer una razón por el fracaso de la validación. Dado que esta clase sólo define un mensaje de fallo, no es necesario darle a _error() el nombre de la plantilla del mensaje de fallo. Escribiendo una Clase de Validación habiendo Condiciones Dependientes El siguiente ejemplo muestra un conjunto de reglas de validación más complejo, donde es necesario que el valor de entrada ser numérico y dentro del límite de un rango de valores mínimos y máximos. Un valor de entrada podría fallar en la validación exactamente por una de las siguientes razones: El valor de entrada no es numérico. El valor de entrada es menor que el valor mínimo permitido. El valor de entrada es mayor que el valor máximo permitido. Estas razones en el fallo de validación, son traducidas a las definiciones en la clase: 'minimum', 'max' => 'maximum' ); protected $_messageTemplates = array( self::MSG_NUMERIC => "'%value%' no es numérico", self::MSG_MINIMUM => "'%value%' debe ser al menos '%min%'", self::MSG_MAXIMUM => "'%value%' debe ser no mayor a '%max%'" ); public function isValid($value) { $this->_setValue($value); if (!is_numeric($value)) { $this->_error(self::MSG_NUMERIC); return false; } if ($value < $this->minimum) { $this->_error(self::MSG_MINIMUM); return false; } if ($value > $this->maximum) { $this->_error(self::MSG_MAXIMUM); return false; } return true; } } ]]> Las propiedades públicas $minimum y $maximum se han establecido para proporcionar los límites mínimo y máximo, respectivamente, de un valor a validar. La clase también define dos variables de mensajes que corresponden a las propiedades públicas y permiten usar min y max en plantillas de mensajes como parámetros mágicos, al igual que con value. Tenga en cuenta que si cualquiera de las comprobaciones de validación falla en isValid(), ya está preparado un mensaje apropiado, y el método inmediatamente devuelve false. Estas reglas de validación son por lo tanto secuencialmente dependientes. Es decir, si uno de los tests falla, no hay necesidad de poner a prueba las posteriores reglas de validación. Sin embargo, esta necesidad no será el caso. El siguiente ejemplo ilustra cómo escribir una clase con reglas de validación independientes, donde el objeto validación puede devolver múltiples razones por las cuales fracasó un intento de validación en particular. Validación con Condiciones Independientes, Múltiples Razones del Fracaso Considere escribir una clase de validación y control de contraseñas - cuando es necesario que un usuario elija una contraseña que cumple determinados criterios para ayudar a tener cuentas de usuario seguras. Supongamos que la seguridad de la contraseña aplica criterios que fuerzan a lo siguiente: debe tener al menos una longitud de 8 caracteres, contener al menos una letra en mayúscula, contener al menos una letra en minúscula, contener al menos un dígito. La siguiente clase implementa estos criterios de validación: "'%value%' debe tener al menos una longitud de 8 caracteres", self::UPPER => "'%value%' debe contener al menos una letra en mayúscula", self::LOWER => "'%value%' debe contener al menos una letra en minúscula", self::DIGIT => "'%value%' debe contener al menos un dígito" ); public function isValid($value) { $this->_setValue($value); $isValid = true; if (strlen($value) < 8) { $this->_error(self::LENGTH); $isValid = false; } if (!preg_match('/[A-Z]/', $value)) { $this->_error(self::UPPER); $isValid = false; } if (!preg_match('/[a-z]/', $value)) { $this->_error(self::LOWER); $isValid = false; } if (!preg_match('/\d/', $value)) { $this->_error(self::DIGIT); $isValid = false; } return $isValid; } } ]]> Las cuatro pruebas de criterio en isValid() no devuelven inmediatamente false. Esto permite a la clase de validación a proporcionar todas las razones por las que la contraseña de entrada no cumplió los requisitos de validación. Si, por ejemplo, un usuario ingresara el string "#$%" como contraseña, isValid() causaría que los cuatro mensajes de fracaso de validación sean devueltos por un llamado posterior a getMessages(). Zend_Version Zend_View Zend_Wildfire Zend_XmlRpc Zend_XmlRpc_Client Introdución Zend Framework provee soporte para consumo remoto para servicios XML-RPC como un cliente en el paquete Zend_XmlRpc_Client . Su mejor característica es la conversión atuomática de tipos entre PHP y XML-RPC, un servidor de objeto proxy, y acceso a capacidades de instrospección del servidor. Method Calls El constructor de Zend_XmlRpc_Client recibe la URL del servidor XML-RPC como su primer parámetro. La nueva instacia devuelta pu7ede ser usada para llamar cualquier número de métodos remotos en el punto final. Para llamar un método remoto con el cliente XML-RPC, instáncealo y usa el método de instancia call() . El código de ejemplo a continuación utiliza una demostración en el servidor XML-RPC en el sitio wed del Zend Framework . Puede utilizarlo para probar o explorar los componentes Zend_XmlRpc. XML-RPC Method Call call('test.sayHello'); // hello ]]> El valor XML-RPC devuelto desde la llamada al método remoto automáticamente será convertida al tipo nativo PHP equivalente . En el ejemplo anterior, es devuelto un string PHP y está listo para ser usada inmediatamente. El primer parámetro del método call() recibe el nombre del método remoto a llamar. Si el método remoto requiere algún parámetro, este puede ser enviado por el suministro de un segundo, parámetro opcional a call() con un array de valores para pasar el método remoto: XML-RPC Method Call with Parameters call('test.sayHello', array($arg1, $arg2)); // $result es un tipo nativo PHP ]]> si el método remoto no requiere parámetros, este parámetro opcional podrá ser excluido o se puede pasar un array() vacío. El array de parámeters para el método repoto puede contener tipos nativos PHPs, objetos Zend_XmlRpc_Value , o una combinación de estos. El método call() convertirá automáticamente la respuesta XML-RPC y devolverá su tipo nativo PHP equivalente. Un objeto Zend_XmlRpc_Response para el valor devuelto también estará disponible para llamar el método getLastResponse() después de la llamada. Tipos y Conversiones Algunas llamadas a métodos remoto requieren parámetros. Estos son dados al método call() de Zend_XmlRpc_Client como un array en el segundo parámetro. Cada parámetro puede ser dado como cualquier tipo nativo PHP ya que este será convertido automáticamente, o como un objeto que representa un tipo específico de XML-RPC (uno de los objetos Zend_XmlRpc_Value). Tipos Nativos PHP como Parámetro Los parámetros pueden ser pasados a call() como variables PHP nativas, ya sea un string, integer, float, boolean, array, o un object. En este caso, cada tipo PHP nativo será autodetectada y convertida en uno de los tipos XML-RPC de acuerdo con esta tabla: Tipos de Conversión entre PHP y XML-RPC Tipo Nativo PHP Tipo XML-RPC integer int double double boolean boolean string string array array array asociativo struct object array
    ¿A qué tipo se convierten los arrays Vacios? Pasar un array vacío a un método XML-RPC es problemático, as it could represent either an array or a struct. Zend_XmlRpc_Client detects such conditions and makes a request to the server's system.methodSignature method to determine the appropriate XML-RPC type to cast to. However, this in itself can lead to issues. First off, servers that do not support system.methodSignature will log failed requests, and Zend_XmlRpc_Client will resort to casting the value to an XML-RPC array type. Additionally, this means that any call with array arguments will result in an additional call to the remote server. To disable the lookup entirely, you can call the setSkipSystemLookup() method prior to making your XML-RPC call: setSkipSystemLookup(true); $result = $client->call('foo.bar', array(array())); ]]>
    <code>Zend_XmlRpc_Value</code> Objects as Parameters Parameters may also be created as Zend_XmlRpc_Value instances to specify an exact XML-RPC type. The primary reasons for doing this are: When you want to make sure the correct parameter type is passed to the procedure (i.e. the procedure requires an integer and you may get it from a database as a string) When the procedure requires base64 or dateTime.iso8601 type (which doesn't exists as a PHP native type) When auto-conversion may fail (i.e. you want to pass an empty XML-RPC struct as a parameter. Empty structs are represented as empty arrays in PHP but, if you give an empty array as a parameter it will be auto-converted to an XML-RPC array since it's not an associative array) There are two ways to create a Zend_XmlRpc_Value object: instantiate one of the Zend_XmlRpc_Value subclasses directly, or use the static factory method Zend_XmlRpc_Value::getXmlRpcValue(). <code>Zend_XmlRpc_Value</code> Objects for XML-RPC Types XML-RPC Type Zend_XmlRpc_Value Constant Zend_XmlRpc_Value Object int Zend_XmlRpc_Value::XMLRPC_TYPE_INTEGER Zend_XmlRpc_Value_Integer double Zend_XmlRpc_Value::XMLRPC_TYPE_DOUBLE Zend_XmlRpc_Value_Double boolean Zend_XmlRpc_Value::XMLRPC_TYPE_BOOLEAN Zend_XmlRpc_Value_Boolean string Zend_XmlRpc_Value::XMLRPC_TYPE_STRING Zend_XmlRpc_Value_String base64 Zend_XmlRpc_Value::XMLRPC_TYPE_BASE64 Zend_XmlRpc_Value_Base64 dateTime.iso8601 Zend_XmlRpc_Value::XMLRPC_TYPE_DATETIME Zend_XmlRpc_Value_DateTime array Zend_XmlRpc_Value::XMLRPC_TYPE_ARRAY Zend_XmlRpc_Value_Array struct Zend_XmlRpc_Value::XMLRPC_TYPE_STRUCT Zend_XmlRpc_Value_Struct
    Automatic Conversion When building a new Zend_XmlRpc_Value object, its value is set by a PHP type. The PHP type will be converted to the specified type using PHP casting. For example, if a string is given as a value to the Zend_XmlRpc_Value_Integer object, it will be converted using (int)$value.
    Server Proxy Object Another way to call remote methods with the XML-RPC client is to use the server proxy. This is a PHP object that proxies a remote XML-RPC namespace, making it work as close to a native PHP object as possible. To instantiate a server proxy, call the getProxy() instance method of Zend_XmlRpc_Client. This will return an instance of Zend_XmlRpc_Client_ServerProxy. Any method call on the server proxy object will be forwarded to the remote, and parameters may be passed like any other PHP method. Proxy the Default Namespace getProxy(); // Proxy the default namespace $hello = $server->test->sayHello(1, 2); // test.Hello(1, 2) returns "hello" ]]> The getProxy() method receives an optional argument specifying which namespace of the remote server to proxy. If it does not receive a namespace, the default namespace will be proxied. In the next example, the test namespace will be proxied: Proxy Any Namespace getProxy('test'); // Proxy the "test" namespace $hello = $test->sayHello(1, 2); // test.Hello(1,2) returns "hello" ]]> If the remote server supports nested namespaces of any depth, these can also be used through the server proxy. For example, if the server in the example above had a method test.foo.bar(), it could be called as $test->foo->bar(). Error Handling Two kinds of errors can occur during an XML-RPC method call: HTTP errors and XML-RPC faults. The Zend_XmlRpc_Client recognizes each and provides the ability to detect and trap them independently. HTTP Errors If any HTTP error occurs, such as the remote HTTP server returns a 404 Not Found, a Zend_XmlRpc_Client_HttpException will be thrown. Handling HTTP Errors call('bar', array($arg1, $arg2)); } catch (Zend_XmlRpc_Client_HttpException $e) { // $e->getCode() returns 404 // $e->getMessage() returns "Not Found" } ]]> Regardless of how the XML-RPC client is used, the Zend_XmlRpc_Client_HttpException will be thrown whenever an HTTP error occurs. XML-RPC Faults An XML-RPC fault is analogous to a PHP exception. It is a special type returned from an XML-RPC method call that has both an error code and an error message. XML-RPC faults are handled differently depending on the context of how the Zend_XmlRpc_Client is used. When the call() method or the server proxy object is used, an XML-RPC fault will result in a Zend_XmlRpc_Client_FaultException being thrown. The code and message of the exception will map directly to their respective values in the original XML-RPC fault response. Handling XML-RPC Faults call('badMethod'); } catch (Zend_XmlRpc_Client_FaultException $e) { // $e->getCode() returns 1 // $e->getMessage() returns "Unknown method" } ]]> When the call() method is used to make the request, the Zend_XmlRpc_Client_FaultException will be thrown on fault. A Zend_XmlRpc_Response object containing the fault will also be available by calling getLastResponse(). When the doRequest() method is used to make the request, it will not throw the exception. Instead, it will return a Zend_XmlRpc_Response object returned will containing the fault. This can be checked with isFault() instance method of Zend_XmlRpc_Response. Server Introspection Some XML-RPC servers support the de facto introspection methods under the XML-RPC system. namespace. Zend_XmlRpc_Client provides special support for servers with these capabilities. A Zend_XmlRpc_Client_ServerIntrospection instance may be retrieved by calling the getIntrospector() method of Zend_XmlRpcClient. It can then be used to perform introspection operations on the server. From Request to Response Under the hood, the call() instance method of Zend_XmlRpc_Client builds a request object (Zend_XmlRpc_Request) and sends it to another method, doRequest(), that returns a response object (Zend_XmlRpc_Response). The doRequest() method is also available for use directly: Processing Request to Response setMethod('test.sayHello'); $request->setParams(array('foo', 'bar')); $client->doRequest($request); // $server->getLastRequest() returns instanceof Zend_XmlRpc_Request // $server->getLastResponse() returns instanceof Zend_XmlRpc_Response ]]> Whenever an XML-RPC method call is made by the client through any means, either the call() method, doRequest() method, or server proxy, the last request object and its resultant response object will always be available through the methods getLastRequest() and getLastResponse() respectively. HTTP Client and Testing In all of the prior examples, an HTTP client was never specified. When this is the case, a new instance of Zend_Http_Client will be created with its default options and used by Zend_XmlRpc_Client automatically. The HTTP client can be retrieved at any time with the getHttpClient() method. For most cases, the default HTTP client will be sufficient. However, the setHttpClient() method allows for a different HTTP client instance to be injected. The setHttpClient() is particularly useful for unit testing. When combined with the Zend_Http_Client_Adapter_Test, remote services can be mocked out for testing. See the unit tests for Zend_XmlRpc_Client for examples of how to do this.
    Zend Framework Requirements Zend Framework requires a PHP 5 interpreter with a web server configured to handle PHP scripts correctly. Some features require additional extensions or web server features; in most cases the framework can be used without them, although performance may suffer or ancillary features may not be fully functional. An example of such a dependency is mod_rewrite in an Apache environment, which can be used to implement "pretty URL's" like "http://www.example.com/user/edit". If mod_rewrite is not enabled, ZF can be configured to support URL's such as "http://www.example.com?controller=user&action=edit". Pretty URL's may be used to shorten URL's for textual representation or search engine optimization (SEO), but they do not directly affect the functionality of the application. PHP Version Zend recommends PHP 5.2.3 or higher for critical security and performance enhancements, although Zend Framework requires only PHP 5.1.4 or later. Zend Framework has an extensive collection of unit tests, which you can run using PHPUnit 3.0 or later. PHP Extensions You will find a table listing all extensions typically found in PHP and how they are used in Zend Framework below. You should verify that the extensions on which ZF components you'll be using in your application are available in your PHP environments. Many applications will not require every extension listed below. A dependency of type "hard" indicates that the components or classes cannot function properly if the respective extension is not available, while a dependency of type "soft" indicates that the component may use the extension if it is available but will function properly if it is not. Many components will automatically use certain extensions if they are available to optimize performance but will execute code with similar functionality in the component itself if the extensions are unavailable. PHP Extensions Used in Zend Framework by Component Extension Dependency Type Used by Zend Framework Components apc Hard Zend_Cache_Backend_Apc bcmath Soft Zend_Locale bitset Soft Zend_Search_Lucene bz2 --- --- calendar --- --- com_dotnet --- --- ctype Hard Zend_Auth_Adapter_Http Zend_Gdata Zend_Http_Client Zend_Pdf Zend_Rest_Client Zend_Rest_Server Zend_Search_Lucene Zend_Uri Zend_Validate curl Hard Zend_Http_Client_Adapter_Curl date --- --- dba --- --- dbase --- --- dom Hard Zend_Feed Zend_Gdata Zend_Log_Formatter_Xml Zend_Rest_Server Zend_Search_Lucene Zend_Service_Amazon Zend_Service_Delicious Zend_Service_Flickr Zend_Service_Simpy Zend_Service_Yahoo Zend_XmlRpc exif --- --- fbsql --- --- fdf --- --- filter --- --- ftp --- --- gd Hard Zend_Pdf gettext --- --- gmp --- --- hash Hard Zend_Auth_Adapter_Http ibm_db2 Hard Zend_Db_Adapter_Db2 iconv Hard Zend_Currency Zend_Locale_Format Zend_Mime Zend_Pdf Zend_Search_Lucene Zend_Service_Audioscrobbler Zend_Service_Flickr Zend_XmlRpc_Client imap --- --- informix --- --- interbase Hard Zend_Db_Adapter_Firebird json Soft Zend_Json ldap --- --- libxml Hard DOM SimpleXML XSLT mbstring Hard Zend_Feed mcrypt --- --- memcache Hard Zend_Cache_Backend_Memcached mhash --- --- mime_magic Hard Zend_Http_Client ming --- --- msql --- --- mssql --- --- mysql --- --- mysqli Hard Zend_Db_Adapter_Mysqli ncurses --- --- oci8 Hard Zend_Db_Adapter_Oracle odbc --- --- openssl --- --- pcntl --- --- pcre Hard Virtually all components pdo Hard All PDO database adapters pdo_dblib --- --- pdo_firebird --- --- pdo_mssql Hard Zend_Db_Adapter_Pdo_Mssql pdo_mysql Hard Zend_Db_Adapter_Pdo_Mysql pdo_oci Hard Zend_Db_Adapter_Pdo_Oci pdo_pgsql Hard Zend_Db_Adapter_Pdo_Pgsql pdo_sqlite Hard Zend_Db_Adapter_Pdo_Sqlite pgsql --- --- posix Soft Zend_Mail pspell --- --- readline --- --- recode --- --- Reflection Hard Zend_Controller Zend_Filter Zend_Filter_Input Zend_Json Zend_Log Zend_Rest_Server Zend_Server_Reflection Zend_Validate Zend_View Zend_XmlRpc_Server session Hard Zend_Controller_Action_Helper_Redirector Zend_Session shmop --- SimpleXML Hard Zend_Config_Xml Zend_Feed Zend_Rest_Client Zend_Service_Audioscrobbler Zend_XmlRpc soap Hard Zend_Service_StrikeIron sockets --- --- SPL Hard Virtually all components SQLite Hard Zend_Cache_Backend_Sqlite standard Hard Virtually all components sybase --- --- sysvmsg --- --- sysvsem --- -- sysvshm --- --- tidy --- --- tokenizer --- --- wddx --- --- xml Hard Zend_Translate_Adapter_Qt Zend_Translate_Adapter_Tmx Zend_Translate_Adapter_Xliff XMLReader --- --- xmlrpc --- --- XMLWriter --- --- xsl --- --- zip --- --- zlib Hard Zend_Pdf Memcache
    Zend Framework Components Below is a table that lists all available Zend Framework Components and which PHP extension they need. This can help guide you to know which extensions are required for your application. Not all extensions used by Zend Framework are required for every application. A dependency of type "hard" indicates that the components or classes cannot function properly if the respective extension is not available, while a dependency of type "soft" indicates that the component may use the extension if it is available but will function properly if it is not. Many components will automatically use certain extensions if they are available to optimize performance but will execute code with similar functionality in the component itself if the extensions are unavailable. Zend Framework Components and the PHP Extensions they use Zend Framework Components Dependency Type Subclass PHP Extension All Components Hard --- pcre SPL standard Zend_Acl --- --- --- Zend_Auth Hard Zend_Auth_Adapter_Http ctype hash Zend_Cache Hard Zend_Cache_Backend_Apc apc Zend_Cache_Backend_Memcached memcache Zend_Cache_Backend_Sqlite sqlite Zend_Cache_Backend_Zlib zlib Zend_Config Hard Zend_Config_Xml libxml SimpleXML Zend_Console_Getopt --- --- --- Zend_Controller Hard --- Reflection Zend_Controller_Action_Helper_Redirector session Zend_Currency Hard --- iconv Zend_Date --- --- --- Zend_Db Hard All PDO Adapters pdo Zend_Db_Adapter_Db2 ibm_db2 Zend_Db_Adapter_Mysqli mysqli Zend_Db_Adapter_Oracle oci8 Zend_Db_Adapter_Pdo_Mssql pdo_mssql Zend_Db_Adapter_Pdo_Mysql pdo_mysql Zend_Db_Adapter_Pdo_Oci pdo_oci Zend_Db_Adapter_Pdo_Pgsql pdo_pgsql Zend_Db_Adapter_Pdo_Sqlite pdo_sqlite Zend_Debug --- --- --- Zend_Exception --- --- --- Zend_Feed Hard --- dom libxml mbstring SimpleXML Zend_Filter Hard --- Reflection Zend_Form --- --- --- Zend_Gdata Hard Zend_Gdata_App ctype --- dom libxml Zend_Http Hard Zend_Http_Client_Adapter_Curl curl Zend_Http_Client ctype mime_magic Zend_InfoCard --- --- --- Zend_Json Soft --- json Hard --- Reflection Zend_Layout --- --- --- Zend_Ldap --- --- --- Zend_Loader --- --- --- Zend_Locale Soft Zend_Locale_Math bcmath Hard Zend_Locale_Format iconv Zend_Log Hard Zend_Log_Formatter_Xml dom libxml --- Reflection Zend_Mail Soft --- posix Zend_Measure --- --- --- Zend_Memory --- --- --- Zend_Mime Hard Zend_Mime_Decode iconv Zend_OpenId --- --- --- Zend_Pdf Hard --- ctype gd iconv zlib Zend_Registry --- --- --- Zend_Request --- --- --- Zend_Rest Hard Zend_Rest_Client ctype libxml SimpleXML Zend_Rest_Server ctype dom libxml Reflection Zend_Search_Lucene Soft --- bitset Hard ctype dom iconv libxml Zend_Server_Reflection Hard --- Reflection Zend_Service_Akismet --- --- --- Zend_Service_Amazon Hard --- dom libxml Zend_Service_Audioscrobbler Hard --- iconv libxml SimpleXML Zend_Service_Delicious Hard --- dom libxml Zend_Service_Flickr Hard --- dom iconv libxml Zend_Service_Nirvanix --- --- --- Zend_Service_Simpy Hard --- dom libxml Zend_Service_SlideShare --- --- --- Zend_Service_StrikeIron Hard --- soap Zend_Service_Technorati --- --- --- Zend_Service_Yahoo Hard --- dom libxml Zend_Session Hard --- session Zend_TimeSync --- --- --- Zend_Translate Hard Zend_Translate_Adapter_Qt xml Zend_Translate_Adapter_Tmx xml Zend_Translate_Adapter_Xliff xml Zend_Uri Hard --- ctype Zend_Validate Hard --- ctype Reflection Zend_Version --- --- --- Zend_Validate Hard --- Reflection Zend_XmlRpc Hard --- dom libxml SimpleXML Zend_XmlRpc_Client iconv Zend_XmlRpc_Server Reflection
    Zend Framework Dependencies Below you can find a table listing Zend Framework Components and their dependencies to other Zend Framework Components. This can help you if you need to have only single components instead of the complete Zend Framework. A dependency of type "hard" indicates that the components or classes cannot function properly if the respective dependent component is not available, while a dependency of type "soft" indicates that the component may need the dependent component in special situations or with special adapters. Even if it's possible to seperate single components for usage from the complete Zend Framework you should keep in mind that this can lead to problems when files are missed or components are used dynamically. Zend Framework Components and their dependency to other Zend Framework Components Zend Framework Component Dependency Type Dependent Zend Framework Component Zend_Acl Hard Zend_Exception Zend_Auth Hard Zend_Exception Soft Zend_Db Zend_InfoCard Zend_Ldap Zend_OpenId Zend_Session Zend_Cache Hard Zend_Exception Zend_Loader Zend_Config Hard Zend_Exception Zend_Console_Getopt Hard Zend_Exception Zend_Json Zend_Controller Hard Zend_Config Zend_Exception Zend_Filter Zend_Json Zend_Layout Zend_Loader Zend_Registry Zend_Session Zend_Uri Zend_View Zend_Currency Hard Zend_Exception Zend_Locale Zend_Date Hard Zend_Exception Zend_Locale Zend_Db Hard Zend_Config Zend_Exception Zend_Loader Zend_Registry Zend_Debug --- --- Zend_Exception --- --- Zend_Feed Hard Zend_Exception Zend_Http Zend_Loader Zend_Uri Zend_Filter Hard Zend_Exception Zend_Loader Zend_Locale Zend_Validate Zend_Form Hard Zend_Controller Zend_Exception Zend_Filter Zend_Json Zend_Loader Zend_Registry Zend_Session Zend_Validate Zend_Gdata Hard Zend_Exception Zend_Http Zend_Loader Zend_Mime Zend_Version Zend_Http Hard Zend_Exception Zend_Loader Zend_Uri Zend_InfoCard Hard Zend_Loader Zend_Json Hard Zend_Exception Zend_Layout Hard Zend_Controller Zend_Exception Zend_Filter Zend_Loader Zend_View Zend_Ldap Hard Zend_Exception Zend_Loader Hard Zend_Exception Zend_Locale Hard Zend_Exception Zend_Log Hard Zend_Exception Zend_Mail Hard Zend_Exception Zend_Loader Zend_Mime Zend_Validate Zend_Measure Hard Zend_Exception Zend_Locale Zend_Memory Hard Zend_Cache Zend_Exception Zend_Mime Hard Zend_Exception Zend_OpenId Hard Zend_Controller Zend_Exception Zend_Http Zend_Session Zend_Pdf Hard Zend_Exception Zend_Log Zend_Memory Zend_Registry Hard Zend_Exception Zend_Loader Zend_Request --- --- Zend_Rest Hard Zend_Exception Zend_Server Zend_Service Zend_Uri Zend_Search_Lucene Hard Zend_Exception Zend_Server_Reflection Hard Zend_Exception Zend_Service_Akismet Hard Zend_Exception Zend_Http Zend_Uri Zend_Version Zend_Service_Amazon Hard Zend_Exception Zend_Http Zend_Rest Zend_Service_Audioscrobbler Hard Zend_Exception Zend_Http Zend_Service_Delicious Hard Zend_Date Zend_Exception Zend_Http Zend_Json Zend_Rest Zend_Service_Flickr Hard Zend_Exception Zend_Http Zend_Rest Zend_Validate Zend_Service_Nirvanix Hard Zend_Exception Zend_Http Zend_Loader Zend_Service_Simpy Hard Zend_Exception Zend_Http Zend_Rest Zend_Service_SlideShare Hard Zend_Cache Zend_Exception Zend_Http Zend_Service_StrikeIron Hard Zend_Exception Zend_Http Zend_Loader Zend_Service_Technorati Hard Zend_Date Zend_Exception Zend_Http Zend_Locale Zend_Rest Zend_Uri Zend_Service_Yahoo Hard Zend_Exception Zend_Http Zend_Rest Zend_Validate Zend_Session Hard Zend_Exception Zend_Loader Zend_TimeSync Hard Zend_Date Zend_Exception Zend_Loader Zend_Translate Hard Zend_Exception Zend_Loader Zend_Locale Zend_Uri Hard Zend_Exception Zend_Loader Zend_Validate Zend_Validate Soft Zend_Date Zend_Filter Zend_Locale Zend_Registry Hard Zend_Exception Zend_Loader Zend_Version --- --- Zend_View Hard Zend_Controller Zend_Exception Zend_Json Zend_Layout Zend_Loader Zend_Locale Zend_Registry Zend_XmlRpc Hard Zend_Exception Zend_Registry Zend_Server
    Est�ndares de c�digo Zend Framework para PHP Introducci�n Alcance Este documento provee las pautas para el formato del c�digo y la documentaci�n a personas y equipos que contribuyan con Zend Framework. Muchos de los desarrolladores que usan Zend Framework han encontrado �tiles estos est�ndares debido a que el estilo de su c�digo permanece consistente con otros c�digos fuente basados en Zend Framework. Tambi�n debe resaltarse que especificar completamente los est�ndares de c�digo requiere un esfuerzo significativo. Nota: A veces, los desarrolladores consideran el establecimiento de est�ndares m�s importante que lo que el est�ndar sugiere realmente al nivel m�s detallado de dise�o. Estas pautas en los est�ndares de c�digo de Zend Framework han demostrado funcionar bien en otros projectos ZF. Puede modificar estos est�ndares o usarlos en consonancia con los t�rminos de nuestra licencia Temas incluidos en los est�ndares de c�digo ZF: Dar formato a archivos PHP Convenciones de nombrado Estilo de c�digo Documentaci�n integrada Objetivos Los est�ndares de c�digo resultan importantes en cualquier proyecto de desarrollo, pero son especialmente importantes cuando muchos desarrolladores trabajan en el mismo proyecto. Los est�ndares de c�digo ayudan a asegurar que el c�digo tenga una alta calidad, menos errores, y pueda ser mantenido f�cilmente. Formato de archivos PHP General Para archivos que contengan �nicamente c�digo PHP, la etiqueta de cierre ("?>") no est� permitida. No es requerida por PHP, y omitirla evita la inyecci�n de espacios en blanco en la respuesta. IMPORTANTE: La inclusi�n de datos binarios arbitrarios permitidos por __HALT_COMPILER() est� prohibida en los archivos PHP de Zend Framework, as� como en cualquier fichero derivado. El uso de esta caracter�stica s�lo est� permitido en algunos scripts de instalaci�n. Identaci�n La identaci�n suele est�r compuesta por 4 espacios. Las tabulaciones no est�n permitidas. Tama�o m�ximo de l�nea La longitud recomendable para una l�nea de c�digo es de 80 caracteres. Esto significa que los desarrolladores de Zend deber�an intentar mantener cada l�nea de su c�digo por debajo de los 80 caracteres, siempre que sea posible. No obstante, l�neas m�s largas pueden ser aceptables en algunas situaciones. El tama�o m�ximo de cualquier l�nea de c�digo PHP es de 120 caracteres. Final de l�nea El Final de L�nea sigue la convenci�n de archivos de texto Unix. Las l�neas deben acabar con un car�cter linefeed (LF). Los caracteres Linefeed est�n representados con el n�mero 10 ordinal, o el n�mero 0x0A hexadecimal. Nota: No use retornos de carro (carriage returns, CR) como en las fuentes de Apple (0x0D) o la combinaci�n de retorno de carro/linefeed (CRLF) est�ndar para sistemas operativos Windows (0x0D, 0x0A). Convenciones de nombrado Clases Zend Framework se estandariza en una convenci�n de nombrado de clases donde los nombres de las clases apuntan directamente a las carpetas en las que est�n contenidas. La carpeta ra�z de la librer�a est�ndar de ZF es la carpeta "Zend/", mientras que la carpeta ra�z de las librer�as extra de ZF es la carpeta "ZendX/". Todas las clases Zend Framework est�n almacenadas jer�rquicamente bajo estas carpetas ra�z. Los nombres de clase pueden contener s�lo caracteres alfanum�ricos. Los n�meros est�n permitidos en los nombres de clase, pero desaconsejados en la mayor�a de casos. Las barras bajas (_) est�n permitidas s�lo como separador de ruta (el archivo "Zend/Db/Table.php" debe apuntar al nombre de clase "Zend_Db_Table"). Si el nombre de una clase est� compuesto por m�s de una palabra, la primera letra de cada palabra debe aparecer en may�sculas. Poner en may�sculas las letras siguientes no est� permitido, ej: "Zend_PDF" no est� permitido, mientras que "Zend_Pdf" es admisible. Estas convenciones definen un mecanismo de pseudo-espacio de nombres para Zend Framework. Zend Framework adoptar� la funcionalidad PHP de espacio de nombres cuando est� disponible y sea factible su uso en las aplicaciones de nuestros desarrolladores. Vea los nombres de clase en las librer�as estandar y adicionales (extras) como ejemplos de esta convenci�n de nombres. IMPORTANTE: El c�digo que deba distribuirse junto a las librer�as ZF, pero no forma parte de las librer�as est�ndar o extras de Zend (e.g.: c�digo o librer�as que no est�n distribu�das por Zend) no puede empezar nunca por "Zend_" o "ZendX_". Nombres de archivo Para cualquier otro archivo, s�lo caracteres alfanum�ricos, barras bajas (_) y guiones (-) est�n permitidos. Los espacios en blanco est�n estrictamente prohibidos. Cualquier archivo que contenga c�digo PHP debe terminar con la extensi�n ".php", con la excepci�n de los scripts de vista. Los siguientes ejemplos muestran nombres de archivo admisibles para clases de Zend Framework..: Los nombres de archivo deben apuntar a nombres de clases como se describe arriba. Funciones y M�todos Los nombres de funciones pueden contener �nicamente caracteres alfanum�ricos. Las barras bajas (_) no est�n permitidas. Los n�meros est�n permitidos en los nombres de funci�n pero no se aconseja en la mayor�a de casos. Los nombres de funciones deben empezar siempre con una letra min�scula. Cuando un nombre de funci�n consiste en m�s de una palabra, la primera letra de cada nueva palabra debe estar en may�sculas. Esto es llamado com�nmente como formato "camelCase". Por norma general, se recomienda la elocuencia. Los nombres de funci�n deben ser lo suficientemente elocuentes como para describir su prop�sito y comportamiento. Estos son ejemplos de nombres de funciones admisibles: Para programaci�n orientada a objetos, los accesores para instancia o variables est�ticas deben ir antepuestos con un "get" o un "set". Al implementar patrones de dise�o, tales como el patr�n singleton o el patr�n factory, el nombre del m�todo debe contener en la pr�ctica el nombre del patr�n para describir su comportamiento de form�s m�s completa. Para m�todos en objetos que son declarados con el modificador "private" o "protected", el primer car�cter del nombre de la variable debe ser una barra baja (_). �ste es el �nico uso admisible de una barra baja en un nombre de m�todo. Los m�todos declarados como p�blicos no deber�an contener nunca una barra baja. Las funciones de alcance global (tambi�n llamadas "funciones flotantes") est�n permitidas pero desaconsejadas en la mayor�a de casos. Considere envolver esas funciones en una clase est�tica. Variables Los nombres de variables pueden contener caracteres alfanum�ricos. Las barras bajas (_) no est�n permitidas. Los n�meros est�n permitidos en los nombres de variable pero no se aconseja en la mayor�a de casos. Para las variables de instancia que son declaradas con el modificador "private" o "protected", el primer car�cter de la variable debe ser una �nica barra baja (_). �ste es el �nico caso admisible de una barra baja en el nombre de una variable. Las variables declaradas como "public" no pueden empezar nunca por barra baja. Al igual que los nombres de funciones (ver secci�n 3.3), los nombres de variables deben empezar siempre con una letra en min�scula y seguir la convenci�n "camelCaps". Por norma general, se recomienda la elocuencia. Las variables deber�an ser siempre tan elocuentes como pr�cticas para describir los datos que el desarrollador pretende almacenar en ellas. Variables escuetas como "$i" y "$n" est�n desaconsejadas, salvo para el contexto de los bucles m�s peque�os. Si un bucle contiene m�s de 20 l�neas de c�digo, las variables de �ndice deber�an tener nombres m�s descriptivos. Constantes Las constantes pueden contener tanto caracteres alfanum�ricos como barras bajas (_). Los n�meros est�n permitidos. Todos las letras pertenecientes al nombre de una constante deben aparecer en may�sculas. Las palabras dentro del nombre de una constante deben separarse por barras bajas (_). Por ejemplo, EMBED_SUPPRESS_EMBED_EXCEPTION est� permitido, pero EMBED_SUPPRESSEMBEDEXCEPTION no. Las constantes deben ser definidas como miembros de clase con el modificador "const". Definir constantes en el alcance global con la funci�n "define" est� permitido pero no recomendado. Estilo de c�digo Demarcaci�n de c�digo PHP El c�digo PHP debe estar delimitado siempre por la forma completa de las etiquetas PHP est�ndar: ]]> Las etiquetas cortas (short tags) no se permiten nunca. Para archivos que contengan �nicamente c�digo PHP, la etiqueta de cierrre debe omitirse siempre (Ver ). Cadenas Literales cadena Cuando una cadena es literal (no contiene sustituci�n de variables), el ap�strofo o "comilla" deber�a ser usado siempre para delimitar la cadena: Literales Cadena que Contengan Ap�strofos Cuando el propio literal cadena contega ap�strofos, es permitido delimitar la cadena con "dobles comillas". Esto es especialmente �til para sentencias SQL: Esta sint�xis es preferible a escapar ap�strofes, ya ques mucho m�s f�cil de leer. Sustituci�n de Variables La sustituci�n de variables est� permitida en cualquiera de estas formas: Por consistencia, esta forma no est� permitida: Concatenaci�n de cadenas Las cadenas deben ser concatenadas usando el operador punto ("."). Un espacio debe a�adirse siempre antes y despu�s del operador "." para mejorar la legibilidad: Al concatenar cadenas con el operador ".", se recomienda partir la sentencia en m�ltiples l�neas para mejorar la legibilidad. En estos casos, cada linea sucesiva debe llevar un margen de espacios en blanco de forma que el operador "." est� alineado bajo el operador "=": Arrays Arrays Indexados Num�ricamente No est�n permitidos n�meros negativos como �ndices. Un array indexado puede empezar por cualquier valor no negativo, sin embargo, no se recomiendan �ndices base distintos a 0. Al declarar arrays indexados con la funci�n array, un espacio de separaci�n deben a�adirse despu�s de cada delimitador coma para mejorar la legibilidad: Se permite declarar arrays indexados multil�nea usando la construcci�n "array". En este caso, cada l�nea sucesiva debe ser tabulada con cuatro espacios de forma que el principio de cada l�nea est� alineado: Arrays Asociativos Al declarar arrays asociativos con la construcci�n array, se recomienda partir la declaraci�n en m�ltiples l�neas. En este caso, cada l�nea sucesiva debe ser tabulada con cuatro espacios de forma que tanto las llaves como los valores est�n alineados: 'firstValue', 'secondKey' => 'secondValue'); ]]> Clases Declaraci�n de clases Las Clases deben ser nombradas de acuerdo a las convenciones de nombrado de Zend Framework. La llave "{" deber� escribirse siempre en la l�nea debajo del nombre de la clase ("one true brace"). Cada clase debe contener un bloque de documentaci�n acorde con el est�ndar de PHPDocumentor. Todo el c�digo contenido en una clase debe ser separado con cuatro espacios. �nicamente una clase est� permitida en cada archivo PHP. Incluir c�digo adicional en archivos de clase est� permitido pero desaconsejado. En archivos de ese tipo, dos l�neas en blanco deben separar la clase de cualquier c�digo PHP adicional en el archivo de clase. A continuaci�n se muestra un ejemplo de una declaraci�n de clase admisible: Variables de miembros de clase Las variables de miembros de clase deben ser nombradas de acuerdo con las conveciones de nombrado de variables de Zend Framework. Cualquier variable declarada en una clase debe ser listada en la parte superior de la clase, por encima de las declaraciones de cualquier m�todo. La construcci�n var no est� permitido. Las variables de miembro siempre declaran su visibilidad usando uno los modificadores private, protected, o public. Dar acceso a las variables de miembro declar�ndolas directamente como public est� permitido pero no se aconseja en favor de accesor methods (set/get). Funciones y M�todos Declaraci�n de Funciones y M�todos Las Funciones deben ser nombradas de acuerdo a las convenciones de nombrado de Zend Framework. Los m�todos dentro de clases deben declarar siempre su visibilidad usando un modificador private, protected, o public. Como en las clases, la llave "{" debe ser escrita en la l�nea siguiente al nombre de la funci�n ("one true brace" form). No est� permitido un espacio entre el nombre de la funci�n y el par�ntesis de apertura para los argumentos. Las funciones de alcance global no est�n permitidas. Lo siguiente es un ejemplo de una declaraci�n admisible de una funci�n en una clase: NOTA: El paso por referencia es el �nico mecanismo de paso de par�metros permitido en una declaraci�n de m�todo. La llamada por referencia est� estrictamente prohibida. El valor de retorno no debe estar indicado entre par�ntesis. Esto podr�a afectar a la legibilidad, adem�s de romper el c�digo si un m�todo se modifica posteriormente para que devuelva por referencia. bar); } /** * CORRECTO */ public function bar() { return $this->bar; } } ]]> Uso de Funciones y M�todos Los argumentos de la funci�n tendr�an que estar separados por un �nico espacio posterior despu�s del delimitador coma. A continuaci�n se muestra un ejemplo de una invocaci�n admisible de una funci�n que recibe tres argumentos: La llamada por referencia est� estrictamente prohibida. Vea la secci�n de declaraciones de funciones para el m�todo correcto de pasar argumentos por referencia. Al pasar arrays como argumentos a una funci�n, la llamada a la funci�n puede incluir el indicador "hint" y puede separarse en m�ltiples l�neas para aumentar la legibilidad. En esos casos, se aplican las pautas normales para escribir arrays: Sentencias de Control If/Else/Elseif Las sentencias de control basadas en las construcciones if y elseif deben tener un solo espacio en blanco antes del par�ntesis de apertura del condicional y un solo espacio en blanco despu�s del par�ntesis de cierre. Dentro de las sentencias condicionales entre par�ntesis, los operadores deben separarse con espacios, por legibilidad. Se aconseja el uso de par�ntesis internos para mejorar la agrupaci�n l�gica en expresiones condicionales m�s largas. La llave de apertura "{" se escribe en la misma l�nea que la sentencia condicional. La llave de cierre "}" se escribe siempre en su propia l�nea. Cualquier contenido dentro de las llaves debe separarse con cuatro espacios en blanco. Para las declaraciones "if" que incluyan "elseif" o "else", las convenciones de formato son similares a la construcci�n "if". Los ejemplos siguientes demuestran el formato correcto para declaraciones "if" con construcciones "else" y/o "elseif": PHP permite escribir sentencias sin llaves -{}- en algunas circunstancias. Este est�ndar de c�digo no hace ninguna diferenciaci�n- toda sentencia "if", "elseif" o "else" debe usar llaves. El uso de la construcci�n "elseif" est� permitido pero no se aconseja, en favor de la combinaci�n "else if". Switch Las declaraciones de control escritas con la declaraci�n "switch" deben tener un �nico espacio en blanco antes del par�ntesis de apertura del condicional y despu�s del par�ntesis de cierre. Todo contenido dentro de una declaraci�n "switch" debe separarse usando cuatro espacios. El contenido dentro de cada declaraci�n "case" debe separarse usando cuatro espacios adicionales. La construcci�n default no debe omitirse nunca en una declaraci�n switch. NOTA: En ocasiones, resulta �til escribir una declaraci�n case que salta al siguiente case al no incluir un break o return dentro de ese case. Para distinguir estos casos de posibles errores, cualquier declaraci�n donde break o return sean omitidos deber�n contener un comentario indicando que se omitieron intencionadamente. Documentaci�n integrada Formato de documentaci�n Todos los bloques de documentaci�n ("docblocks") deben ser compatibles con el formato de phpDocumentor. Describir el formato de phpDocumentor est� fuera del alcance de este documento. Para m�s informaci�n, visite: http://phpdoc.org/ Todos los archivos de clase deben contener un bloque de documentaci�n "a nivel de archivo" al principio de cada archivo y un bloque de documentaci�n "a nivel de clase" inmediatamente antes de cada clase. Ejemplo de estos bloques de documentaci�n pueden encontrarse debajo. Archivo Cada archivo que contenga c�digo PHP debe tener un bloque de documentaci�n al principio del archivo que contenga como m�nimo las siguientes etiquetas phpDocumentor: Clases Cada clase debe contener un bloque de documentaci�n que contenga como m�nimo las siguientes etiquetas phpDocumentor: Funciones Cada funci�n, incluyendo m�todos de objeto, debe contener un bloque de documentaci�n que contenga como m�nimo: Una descripci�n de la funci�n Todos los argumentos Todos los posibles valores de retorno No es necesario incluir la etiqueta "@access" si el nivel de acceso es conocido de antemano por el modificador "public", "private", o "protected" usado para declarar la funci�n. Si una funci�n/m�todo puede lanzar una excepci�n, utilice @throws para todos los tipos de excepciones conocidas: Guía de rendimiento de Zend Framework Introduction The purpose of this appendix is to provide some concrete strategies for improving the performance of your Zend Framework applications. The guide is presented in a "Question:Answer" format, and broken into areas of concern. Class Loading Anyone who ever performs profiling of a Zend Framework application will immediately recognize that class loading is relatively expensive in Zend Framework. Between the sheer number of class files that need to be loaded for many components, to the use of plugins that do not have a 1:1 relationship between their class name and the filesystem, the various calls to include_once and require_once can be problematic. This chapter intends to provide some concrete solutions to these issues. How can I optimize my include_path? One trivial optimization you can do to increase the speed of class loading is to pay careful attention to your include_path. In particular, you should do four things: use absolute paths (or paths relative to absolute paths), reduce the number of include paths you define, have your Zend Framework include_path as early as possible, and only include the current directory path at the end of your include_path. Use absolute paths While this may seem a micro-optimization, the fact is that if you don't, you'll get very little benefit from PHP's realpath cache, and as a result, opcode caching will not perform nearly as you may expect. There are two easy ways to ensure this. First, you can hardcode the paths in your php.ini, httpd.conf, or .htaccess. Second, you can use PHP's realpath() function when setting your include_path: You can use relative paths -- so long as they are relative to an absolute path: However, even so, it's typically a trivial task to simply pass the path to realpath(). Reduce the number of include paths you define Include paths are scanned in the order in which they appear in the include_path. Obviously, this means that you'll get a result faster if the file is found on the first scan rather than the last. Thus, a rather obvious enhancement is to simply reduce the number of paths in your include_path to only what you need. Look through each include_path you've defined, and determine if you actually have any functionality in that path that is used in your application; if not, remove it. Another optimization is to combine paths. For instance, Zend Framework follows PEAR naming conventions; thus, if you are using PEAR libraries (or libraries from another framework or component library that follows PEAR CS), try to put all of these libraries on the same include_path. This can often be achieved by something as simple as symlinking one or more libraries into a common directory. Define your Zend Framework include_path as early as possible Continuing from the previous suggestion, another obvious optimization is to define your Zend Framework include_path as early as possible in your include_path. In most cases, it should be the first path in the list. This ensures that files included from Zend Framework are found on the first scan. Define the current directory last, or not at all Most include_path examples show using the current directory, or '.'. This is convenient for ensuring that scripts in the same directory as the file requiring them can be loaded. However, these same examples typically show this path item as the first item in the include_path -- which means that the current directory tree is always scanned first. In most cases, with Zend Framework applications, this is not desired, and the path may be safely pushed to the last item in the list. Example: Optimized include_path Let's put all of these suggestions together. Our assumption will be that you are using one or more PEAR libraries in conjunction with Zend Framework -- perhaps the PHPUnit and Archive_Tar libraries -- and that you occasionally need to include files relative to the current file. First, we'll create a library directory in our project. Inside that directory, we'll symlink our Zend Framework's library/Zend directory, as well as the necessary directories from our PEAR installation: This allows us to add our own library code if necessary, while keeping shared libraries intact. Next, we'll opt to create our include_path programmatically within our public/index.php file. This allows us to move our code around on the filesystem, without needing to edit the include_path every time. We'll borrow ideas from each of the suggestions above: we'll use absolute paths, as determined using realpath(); we'll include the Zend Framework include path early; we've already consolidated include_paths; and we'll put the current directory as the last path. In fact, we're doing really well here -- we're going to end up with only two paths. How can I eliminate unnecessary require_once statements? Lazy loading is an optimization technique designed to push the expensive operation of loading a class file until the last possible moment -- i.e., when instantiating an object of that class, calling a static class method, or referencing a class constant or static property. PHP supports this via autoloading, which allows you to define one or more callbacks to execute in order to map a class name to a file. However, most benefits you may reap from autoloading are negated if your library code is still performing require_once calls -- which is precisely the case with Zend Framework. So, the question is: how can you eliminate those require_once calls in order to maximize autoloader performance? Strip require_once calls with find and sed An easy way to strip require_once calls is to use the unix utilities 'find' and 'sed' in conjunction to comment out each call. Try executing the following statements (where '%' indicates the shell prompt): This one-liner (broken into two lines for readability) iterates through each PHP file and tells it to replace each instance of 'require_once' with '// require_once', effectively commenting out each such statement. This command could be added to an automated build or release process trivially, helping boost performance in your production application. It should be noted, however, that if you use this technique, you must utilize autoloading; you can do that from your "public/index.php" file with the following code: How can I speed up plugin loading? Many components have plugins, which allow you to create your own classes to utilize with the component, as well as to override existing, standard plugins shipped with Zend Framework. This provides important flexibility to the framework, but at a price: plugin loading is a fairly expensive task. The plugin loader allows you to register class prefix / path pairs, allowing you to specify class files in non-standard paths. Each prefix can have multiple paths associated with it. Internally, the plugin loader loops through each prefix, and then through each path attached to it, testing to see if the file exists and is readable on that path. It then loads it, and tests to see that the class it is looking for is available. As you might imagine, this can lead to many stat calls on the filesystem. Multiply this by the number of components that use the PluginLoader, and you get an idea of the scope of this issue. At the time of this writing, the following components made use of the PluginLoader: Zend_Controller_Action_HelperBroker: helpers Zend_Dojo: view helpers, form elements and decorators Zend_File_Transfer: adapters Zend_Filter_Inflector: filters (used by the ViewRenderer action helper and Zend_Layout) Zend_Filter_Input: filters and validators Zend_Form: elements, validators, filters, decorators, captcha and file transfer adapters Zend_Paginator: adapters Zend_View: helpers, filters How can you reduce the number of such calls made? Use the PluginLoader include file cache Zend Framework 1.7.0 adds an include file cache to the PluginLoader. This functionality writes "include_once" calls to a file, which you can then include in your bootstrap. While this introduces extra include_once calls to your code, it also ensures that the PluginLoader returns as early as possible. The PluginLoader documentation includes a complete example of its use. Internationalization (i18n) and Localization (l10n) Internationalizing and localizing a site are fantastic ways to expand your audience and ensure that all visitors can get to the information they need. However, it often comes with a performance penalty. Below are some strategies you can employ to reduce the overhead of i18n and l10n. Which translation adapter should I use? Not all translation adapters are made equal. Some have more features than others, and some are more performant than others. Additionally, you may have business requirements that force you to use a particular adapter. However, if you have a choice, which adapters are fastest? Use non-XML translation adapters for greatest speed Zend Framework ships with a variety of translation adapters. Fully half of them utilize an XML format, incurring memory and performance overhead. Fortunately, there are several adapters that utilize other formats that can be parsed much more quickly. In order of speed, from fastest to slowest, they are: Array: this is the fastest, as it is, by definition, parsed into a native PHP format immediately on inclusion. CSV: uses fgetcsv() to parse a CSV file and transform it into a native PHP format. INI: uses parse_ini_file() to parse an INI file and transform it into a native PHP format. This and the CSV adapter are roughly equivalent performance-wise. Gettext: the Zend Framework gettext adapter does not use the gettext extension as it is not threadsafe and does not allow specifying more than one locale per server. As a result, it is slower than using the gettext extension directly, but, because the gettext format is binary, it's faster to parse than XML. If high performance is one of your concerns, we suggest utilizing one of the above adapters. How can I make translation and localization even faster? Maybe, for business reasons, you're limited to an XML-based translation adapter. Or perhaps you'd like to speed things up even more. Or perhaps you want to make l10n operations faster. How can you do this? Use translation and localization caches Both Zend_Translate and Zend_Locale implement caching functionality that can greatly affect performance. In the case of each, the major bottleneck is typically reading the files, not the actual lookups; using a cache eliminates the need to read the translation and/or localization files. You can read about caching of translation and localization strings in the following locations: Zend_Translate adapter caching Zend_Locale caching View Rendering When using Zend Framework's MVC layer, chances are you will be using Zend_View. Zend_View is relatively performant when compared to other view or templating engines; since view scripts are written in PHP, you do not incur the overhead of compiling custom markup to PHP, nor do you need to worry that the compiled PHP is unoptimized. However, Zend_View presents its own issues: extension is done via overloading (view helpers), and a number of view helpers, while carrying out key functionality do so with a performance cost. How can I speed up resolution of view helpers? Most Zend_View "methods" are actually provided via overloading to the helper system. This provides important flexibility to Zend_View; instead of needing to extend Zend_View and provide all the helper methods you may utilize in your application, you can define your helper methods in separate classes and consume them at will as if they were direct methods of Zend_View. This keeps the view object itself relatively thin, and ensures that objects are created only when needed. Internally, Zend_View uses the PluginLoader to look up helper classes. This means that for each helper you call, Zend_View needs to pass the helper name to the PluginLoader, which then needs to determine the class name, load the class file if necessary, and then return the class name so it may be instantiated. Subsequent uses of the helper are much faster, as Zend_View keeps an internal registry of loaded helpers, but if you use many helpers, the calls add up. The question, then, is: how can you speed up helper resolution? Use the PluginLoader include file cache The simplest, cheapest solution is the same as for general PluginLoader performance: use the PluginLoader include file cache. Anecdotal evidence has shown this technique to provide a 25-30% performance gain on systems without an opcode cache, and a 40-65% gain on systems with an opcode cache. Extend Zend_View to provide often used helper methods Another solution for those seeking to tune performance even further is to extend Zend_View to manually add the helper methods they most use in their application. Such helper methods may simply manually instantiate the appropriate helper class and proxy to it, or stuff the full helper implementation into the method. _localHelperObjects)) { $this->_localHelperObjects['url'] = new Zend_View_Helper_Url(); $this->_localHelperObjects['url']->setView($view); } $helper = $this->_localHelperObjects['url']; return $helper->url($urlOptions, $name, $reset, $encode); } /** * Echo a message * * Direct implementation. * * @param string $string * @return string */ public function message($string) { return "

    " . $this->escape($message) . "

    \n"; } } ]]>
    Either way, this technique will substantially reduce the overhead of the helper system by avoiding calls to the PluginLoader entirely, and either benefiting from autoloading or bypassing it altogether.
    How can I speed up view partials? Those who use partials heavily and who profile their applications will often immediately notice that the partial() view helper incurs a lot of overhead, due to the need to clone the view object. Is it possible to speed this up? Use partial() only when really necessary The partial() view helper accepts three arguments: $name: the name of the view script to render $module: the name of the module in which the view script resides; or, if no third argument is provided and this is an array or object, it will be the $model argument. $model: an array or object to pass to the partial representing the clean data to assign to the view. The power and use of partial() come from the second and third arguments. The $module argument allows partial() to temporarily add a script path for the given module so that the partial view script will resolve to that module; the $model argument allows you to explicitly pass variables for use with the partial view. If you're not passing either argument, use render() instead! Basically, unless you are actually passing variables to the partial and need the clean variable scope, or rendering a view script from another MVC module, there is no reason to incur the overhead of partial(); instead, use Zend_View's built-in render() method to render the view script. How can I speed up calls to the action() view helper? Version 1.5.0 introduced the action() view helper, which allows you to dispatch an MVC action and capture its rendered content. This provides an important step towards the DRY principle, and promotes code reuse. However, as those who profile their applications will quickly realize, it, too, is an expensive operation. Internally, the action() view helper needs to clone new request and response objects, invoke the dispatcher, invoke the requested controller and action, etc. How can you speed it up? Use the ActionStack when possible Introduced at the same time as the action() view helper, the ActionStack consists of an action helper and a front controller plugin. Together, they allow you to push additional actions to invoke during the dispatch cycle onto a stack. If you are calling action() from your layout view scripts, you may want to instead use the ActionStack, and render your views to discrete response segments. As an example, you could write a dispatchLoopStartup() plugin like the following to add a login form box to each page: getStack(); $loginRequest = new Zend_Controller_Request_Simple(); $loginRequest->setControllerName('user') ->setActionName('index') ->setParam('responseSegment', 'login'); $stack->pushStack($loginRequest); } public function getStack() { if (null === $this->_stack) { $front = Zend_Controller_Front::getInstance(); if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) { $stack = new Zend_Controller_Plugin_ActionStack(); $front->registerPlugin($stack); } else { $stack = $front->getPlugin('ActionStack') } $this->_stack = $stack; } return $this->_stack; } } ]]> The UserController::indexAction() method might then use the responseSegment parameter to indicate which response segment to render to. In the layout script, you would then simply render that response segment: layout()->login ?> ]]> While the ActionStack still requires a dispatch cycle, this is still cheaper than the action() view helper as it does not need to clone objects and reset internal state. Additionally, it ensures that all pre/post dispatch plugins are invoked, which may be of particular concern if you are using front controller plugins for handling ACLs to particular actions. Favor helpers that query the model over action() In most cases, using action() is simply overkill. If you have most business logic nested in your models and are simply querying the model and passing the results to a view script, it will typically be faster and cleaner to simply write a view helper that pulls the model, queries it, and does something with that information. As an example, consider the following controller action and view script: view->bugs = $model->fetchActive(); } } // bug/list.phtml: echo "
      \n"; foreach ($this->bugs as $bug) { printf("
    • %s: %s
    • \n", $this->escape($bug->id), $this->escape($bug->summary)); } echo "
    \n"; ]]>
    Using action(), you would then invoke it with the following: action('list', 'bug') ?> ]]> This could be refactored to a view helper that looks like the following: \n"; foreach ($model->fetchActive() as $bug) { $html .= sprintf( "
  • %s: %s
  • \n", $this->view->escape($bug->id), $this->view->escape($bug->summary) ); } $html .= "\n"; return $html; } } ]]>
    You would then invoke the helper as follows: bugList() ?> ]]> This has two benefits: it no longer incurs the overhead of the action() view helper, and also presents a more semantically understandable API.
    Copyright Information The following copyrights are applicable to portions of Zend Framework. Copyright © 2005- Zend Technologies Inc. ()